games endpoint and controller

This commit is contained in:
Ravi Prasad
2025-07-16 23:45:31 -05:00
parent 7dcec04f5d
commit 681bf80408
9 changed files with 1734 additions and 3 deletions
+2 -2
View File
@@ -4,8 +4,8 @@ FROM golang:1.24-bullseye AS builder
ENV CGO_ENABLED=1 ENV CGO_ENABLED=1
ENV GOOS=linux ENV GOOS=linux
# Changed from Apple Silicon arm64 config. # Changed from Apple Silicon arm64 config.
ENV GOARCH=amd64 # ENV GOARCH=amd64
# ENV GOARCH=arm64 ENV GOARCH=arm64
WORKDIR /app WORKDIR /app
+349
View File
@@ -0,0 +1,349 @@
package controllers
import (
"strings"
"time"
"github.com/gofiber/fiber/v2"
"gorm.io/gorm"
"github.com/nprasad2077/NBA_Go/models"
"github.com/nprasad2077/NBA_Go/utils"
)
// DTOs (Data Transfer Objects) for API responses
// These structs define the exact JSON output, ensuring team names are abbreviated.
type LineScoreDTO struct {
Team string `json:"team"` // Abbreviation
Q1 int `json:"q1"`
Q2 int `json:"q2"`
Q3 int `json:"q3"`
Q4 int `json:"q4"`
OT1 int `json:"ot1"`
OT2 int `json:"ot2"`
OT3 int `json:"ot3"`
Total int `json:"total"`
}
type PlayerGameBasicStatDTO struct {
PlayerID string `json:"playerId"`
PlayerName string `json:"playerName"`
Team string `json:"team"` // Abbreviation
Status string `json:"status"`
MP string `json:"mp"`
FG int `json:"fg"`
FGA int `json:"fga"`
FGPercent float64 `json:"fgPercent"`
ThreeP int `json:"threeP"`
ThreePA int `json:"threePa"`
ThreePPercent float64 `json:"threePPercent"`
FT int `json:"ft"`
FTA int `json:"fta"`
FTPercent float64 `json:"ftPercent"`
ORB int `json:"orb"`
DRB int `json:"drb"`
TRB int `json:"trb"`
AST int `json:"ast"`
STL int `json:"stl"`
BLK int `json:"blk"`
TOV int `json:"tov"`
PF int `json:"pf"`
PTS int `json:"pts"`
GmSc float64 `json:"gmSc"`
PlusMinus int `json:"plusMinus"`
}
type PlayerGameAdvStatDTO struct {
PlayerID string `json:"playerId"`
PlayerName string `json:"playerName"`
Team string `json:"team"` // Abbreviation
MP string `json:"mp"`
TSPercent float64 `json:"tsPercent"`
EFGPercent float64 `json:"efgPercent"`
ThreePAr float64 `json:"threePAr"`
FTr float64 `json:"fTr"`
ORBPercent float64 `json:"orbPercent"`
DRBPercent float64 `json:"drbPercent"`
TRBPercent float64 `json:"trbPercent"`
ASTPercent float64 `json:"astPercent"`
STLPercent float64 `json:"stlPercent"`
BLKPercent float64 `json:"blkPercent"`
TOVPercent float64 `json:"tovPercent"`
USGPercent float64 `json:"usgPercent"`
ORtg int `json:"oRtg"`
DRtg int `json:"dRtg"`
BPM float64 `json:"bpm"`
}
type TeamGameBasicStatDTO struct {
Team string `json:"team"` // Abbreviation
FG int `json:"fg"`
FGA int `json:"fga"`
FGPercent float64 `json:"fgPercent"`
ThreeP int `json:"threeP"`
ThreePA int `json:"threePa"`
ThreePPercent float64 `json:"threePPercent"`
FT int `json:"ft"`
FTA int `json:"fta"`
FTPercent float64 `json:"ftPercent"`
ORB int `json:"orb"`
DRB int `json:"drb"`
TRB int `json:"trb"`
AST int `json:"ast"`
STL int `json:"stl"`
BLK int `json:"blk"`
TOV int `json:"tov"`
PF int `json:"pf"`
PTS int `json:"pts"`
}
type TeamGameAdvStatDTO struct {
Team string `json:"team"` // Abbreviation
ORtg float64 `json:"oRtg"`
DRtg float64 `json:"dRtg"`
TSPercent float64 `json:"tsPercent"`
EFGPercent float64 `json:"efgPercent"`
ThreePAr float64 `json:"threePAr"`
FTr float64 `json:"fTr"`
ORBPercent float64 `json:"orbPercent"`
DRBPercent float64 `json:"drbPercent"`
TRBPercent float64 `json:"trbPercent"`
ASTPercent float64 `json:"astPercent"`
STLPercent float64 `json:"stlPercent"`
BLKPercent float64 `json:"blkPercent"`
TOVPercent float64 `json:"tovPercent"`
}
// GameResponseDTO is the top-level object for a single game in the response.
type GameResponseDTO struct {
ID uint `json:"id"`
GameID string `json:"gameId"`
Date time.Time `json:"date"`
IsPlayoff bool `json:"isPlayoff"`
StartTimeET string `json:"startTimeET"`
Arena string `json:"arena"`
VisitorTeam string `json:"visitorTeam"`
VisitorPTS int `json:"visitorPts"`
HomeTeam string `json:"homeTeam"`
HomePTS int `json:"homePts"`
GameDuration string `json:"gameDuration"`
BoxScoreURL string `json:"boxScoreUrl"`
LineScores []LineScoreDTO `json:"lineScores,omitempty"`
PlayerGameBasicStats []PlayerGameBasicStatDTO `json:"playerGameBasicStats,omitempty"`
PlayerGameAdvStats []PlayerGameAdvStatDTO `json:"playerGameAdvStats,omitempty"`
TeamGameBasicStats []TeamGameBasicStatDTO `json:"teamGameBasicStats,omitempty"`
TeamGameAdvStats []TeamGameAdvStatDTO `json:"teamGameAdvStats,omitempty"`
}
// GamesResponse is the final paginated structure returned by the API.
type GamesResponse struct {
Data []GameResponseDTO `json:"data"`
Pagination struct {
Total int64 `json:"total"`
Page int `json:"page"`
PageSize int `json:"pageSize"`
Pages int64 `json:"pages"`
} `json:"pagination"`
}
// gameSortMap translates API sort parameters to database column names.
var gameSortMap = map[string]string{
"date": "date",
"visitorPts": "visitor_pts",
"homePts": "home_pts",
"gameDuration": "game_duration",
}
// GetGames godoc
// @Summary Get game data
// @Description Returns a paginated list of games with optional filters and included associations.
// @Tags Games
// @Produce json
// @Param date query string false "Filter by date (YYYY-MM-DD)"
// @Param team query string false "Filter by team abbreviation (e.g., LAL)"
// @Param gameId query string false "Filter by a specific Game ID"
// @Param isPlayoff query bool false "Filter for playoff games"
// @Param page query int false "Page number" default(1)
// @Param pageSize query int false "Page size" default(20)
// @Param sortBy query string false "Field to sort by (e.g., date)" default(date)
// @Param ascending query bool false "Sort ascending" default(false)
// @Param include query string false "Comma-separated list of associations to include (e.g., lineScores,playerGameBasicStats,teamGameAdvStats)"
// @Success 200 {object} controllers.GamesResponse
// @Failure 400 {object} map[string]string
// @Failure 500 {object} map[string]string
// @Router /api/games [get]
func GetGames(db *gorm.DB) fiber.Handler {
return func(c *fiber.Ctx) error {
// --- Build Query ---
query := db.Model(&models.Game{})
// --- Filtering ---
if gameId := c.Query("gameId"); gameId != "" {
query = query.Where("game_id = ?", gameId)
}
if dateStr := c.Query("date"); dateStr != "" {
parsedDate, err := time.Parse("2006-01-02", dateStr)
if err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid date format. Use YYYY-MM-DD."})
}
query = query.Where("date = ?", parsedDate)
}
if teamAbbr := c.Query("team"); teamAbbr != "" {
// Use the helper function to get the full name for the DB query
fullName := utils.GetFullName(strings.ToUpper(teamAbbr))
if fullName != teamAbbr { // Check if a match was found
query = query.Where("visitor_team = ? OR home_team = ?", fullName, fullName)
}
}
if c.Query("isPlayoff") != "" {
query = query.Where("is_playoff = ?", c.QueryBool("isPlayoff"))
}
// --- Preloading Associations ---
includeParam := c.Query("include")
if includeParam != "" {
associations := strings.Split(includeParam, ",")
for _, assoc := range associations {
// Preload the original model association based on the JSON field name
// GORM is smart enough to handle the mapping from struct field to relation
switch strings.TrimSpace(assoc) {
case "lineScores":
query = query.Preload("LineScores")
case "playerGameBasicStats":
query = query.Preload("PlayerGameBasicStats")
case "playerGameAdvStats":
query = query.Preload("PlayerGameAdvStats")
case "teamGameBasicStats":
query = query.Preload("TeamGameBasicStats")
case "teamGameAdvStats":
query = query.Preload("TeamGameAdvStats")
}
}
}
// --- Sorting ---
sortByParam := c.Query("sortBy", "date")
sortBy, ok := gameSortMap[sortByParam]
if !ok {
sortBy = "date"
}
order := sortBy + " DESC"
if c.QueryBool("ascending", false) {
order = sortBy + " ASC"
}
query = query.Order(order)
// --- Pagination & Data Fetching ---
var total int64
query.Count(&total)
page := c.QueryInt("page", 1)
pageSize := c.QueryInt("pageSize", 20)
offset := (page - 1) * pageSize
var games []models.Game
if err := query.Limit(pageSize).Offset(offset).Find(&games).Error; err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
}
// --- Transformation from Model to DTO ---
gameDTOs := make([]GameResponseDTO, len(games))
for i, game := range games {
gameDTOs[i] = toGameResponseDTO(game)
}
// --- Build Final Response ---
resp := GamesResponse{Data: gameDTOs}
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)
}
}
// toGameResponseDTO converts a Game model to its corresponding DTO, including nested associations.
func toGameResponseDTO(game models.Game) GameResponseDTO {
dto := GameResponseDTO{
ID: game.ID,
GameID: game.GameID,
Date: game.Date,
IsPlayoff: game.IsPlayoff,
StartTimeET: game.StartTimeET,
Arena: game.Arena,
VisitorTeam: utils.GetAbbreviation(game.VisitorTeam),
VisitorPTS: game.VisitorPTS,
HomeTeam: utils.GetAbbreviation(game.HomeTeam),
HomePTS: game.HomePTS,
GameDuration: game.GameDuration,
BoxScoreURL: game.BoxScoreURL,
}
// Transform nested slices if they were loaded
if len(game.LineScores) > 0 {
dto.LineScores = make([]LineScoreDTO, len(game.LineScores))
for i, ls := range game.LineScores {
dto.LineScores[i] = LineScoreDTO{
Team: utils.GetAbbreviation(ls.Team),
Q1: ls.Q1, Q2: ls.Q2, Q3: ls.Q3, Q4: ls.Q4,
OT1: ls.OT1, OT2: ls.OT2, OT3: ls.OT3,
Total: ls.Total,
}
}
}
if len(game.PlayerGameBasicStats) > 0 {
dto.PlayerGameBasicStats = make([]PlayerGameBasicStatDTO, len(game.PlayerGameBasicStats))
for i, pbs := range game.PlayerGameBasicStats {
dto.PlayerGameBasicStats[i] = PlayerGameBasicStatDTO{
PlayerID: pbs.PlayerID, PlayerName: pbs.PlayerName, Team: utils.GetAbbreviation(pbs.Team),
Status: pbs.Status, MP: pbs.MP, FG: pbs.FG, FGA: pbs.FGA, FGPercent: pbs.FGPercent,
ThreeP: pbs.ThreeP, ThreePA: pbs.ThreePA, ThreePPercent: pbs.ThreePPercent,
FT: pbs.FT, FTA: pbs.FTA, FTPercent: pbs.FTPercent, ORB: pbs.ORB, DRB: pbs.DRB,
TRB: pbs.TRB, AST: pbs.AST, STL: pbs.STL, BLK: pbs.BLK, TOV: pbs.TOV, PF: pbs.PF,
PTS: pbs.PTS, GmSc: pbs.GmSc, PlusMinus: pbs.PlusMinus,
}
}
}
if len(game.PlayerGameAdvStats) > 0 {
dto.PlayerGameAdvStats = make([]PlayerGameAdvStatDTO, len(game.PlayerGameAdvStats))
for i, pas := range game.PlayerGameAdvStats {
dto.PlayerGameAdvStats[i] = PlayerGameAdvStatDTO{
PlayerID: pas.PlayerID, PlayerName: pas.PlayerName, Team: utils.GetAbbreviation(pas.Team),
MP: pas.MP, TSPercent: pas.TSPercent, EFGPercent: pas.EFGPercent, ThreePAr: pas.ThreePAr,
FTr: pas.FTr, ORBPercent: pas.ORBPercent, DRBPercent: pas.DRBPercent, TRBPercent: pas.TRBPercent,
ASTPercent: pas.ASTPercent, STLPercent: pas.STLPercent, BLKPercent: pas.BLKPercent,
TOVPercent: pas.TOVPercent, USGPercent: pas.USGPercent, ORtg: pas.ORtg, DRtg: pas.DRtg, BPM: pas.BPM,
}
}
}
if len(game.TeamGameBasicStats) > 0 {
dto.TeamGameBasicStats = make([]TeamGameBasicStatDTO, len(game.TeamGameBasicStats))
for i, tbs := range game.TeamGameBasicStats {
dto.TeamGameBasicStats[i] = TeamGameBasicStatDTO{
Team: utils.GetAbbreviation(tbs.Team), FG: tbs.FG, FGA: tbs.FGA, FGPercent: tbs.FGPercent,
ThreeP: tbs.ThreeP, ThreePA: tbs.ThreePA, ThreePPercent: tbs.ThreePPercent,
FT: tbs.FT, FTA: tbs.FTA, FTPercent: tbs.FTPercent, ORB: tbs.ORB, DRB: tbs.DRB,
TRB: tbs.TRB, AST: tbs.AST, STL: tbs.STL, BLK: tbs.BLK, TOV: tbs.TOV, PF: tbs.PF, PTS: tbs.PTS,
}
}
}
if len(game.TeamGameAdvStats) > 0 {
dto.TeamGameAdvStats = make([]TeamGameAdvStatDTO, len(game.TeamGameAdvStats))
for i, tas := range game.TeamGameAdvStats {
dto.TeamGameAdvStats[i] = TeamGameAdvStatDTO{
Team: utils.GetAbbreviation(tas.Team), ORtg: tas.ORtg, DRtg: tas.DRtg, TSPercent: tas.TSPercent,
EFGPercent: tas.EFGPercent, ThreePAr: tas.ThreePAr, FTr: tas.FTr, ORBPercent: tas.ORBPercent,
DRBPercent: tas.DRBPercent, TRBPercent: tas.TRBPercent, ASTPercent: tas.ASTPercent,
STLPercent: tas.STLPercent, BLKPercent: tas.BLKPercent, TOVPercent: tas.TOVPercent,
}
}
}
return dto
}
+1 -1
View File
@@ -25,7 +25,7 @@ services:
db-init: db-init:
build: . build: .
env_file: .env env_file: .env
command: ["/nba_go", "import-data"] command: ["/nba_go"]
depends_on: depends_on:
postgres: postgres:
condition: service_healthy condition: service_healthy
+488
View File
@@ -15,6 +15,104 @@ const docTemplate = `{
"host": "{{.Host}}", "host": "{{.Host}}",
"basePath": "{{.BasePath}}", "basePath": "{{.BasePath}}",
"paths": { "paths": {
"/api/games": {
"get": {
"description": "Returns a paginated list of games with optional filters and included associations.",
"produces": [
"application/json"
],
"tags": [
"Games"
],
"summary": "Get game data",
"parameters": [
{
"type": "string",
"description": "Filter by date (YYYY-MM-DD)",
"name": "date",
"in": "query"
},
{
"type": "string",
"description": "Filter by team abbreviation (e.g., LAL)",
"name": "team",
"in": "query"
},
{
"type": "string",
"description": "Filter by a specific Game ID",
"name": "gameId",
"in": "query"
},
{
"type": "boolean",
"description": "Filter for playoff games",
"name": "isPlayoff",
"in": "query"
},
{
"type": "integer",
"default": 1,
"description": "Page number",
"name": "page",
"in": "query"
},
{
"type": "integer",
"default": 20,
"description": "Page size",
"name": "pageSize",
"in": "query"
},
{
"type": "string",
"default": "date",
"description": "Field to sort by (e.g., date)",
"name": "sortBy",
"in": "query"
},
{
"type": "boolean",
"default": false,
"description": "Sort ascending",
"name": "ascending",
"in": "query"
},
{
"type": "string",
"description": "Comma-separated list of associations to include (e.g., lineScores,playerGameBasicStats,teamGameAdvStats)",
"name": "include",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/controllers.GamesResponse"
}
},
"400": {
"description": "Bad Request",
"schema": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
}
}
}
},
"/api/playeradvancedstats": { "/api/playeradvancedstats": {
"get": { "get": {
"description": "Returns filtered and paginated player advanced stats", "description": "Returns filtered and paginated player advanced stats",
@@ -304,6 +402,393 @@ const docTemplate = `{
} }
} }
}, },
"controllers.GameResponseDTO": {
"type": "object",
"properties": {
"arena": {
"type": "string"
},
"boxScoreUrl": {
"type": "string"
},
"date": {
"type": "string"
},
"gameDuration": {
"type": "string"
},
"gameId": {
"type": "string"
},
"homePts": {
"type": "integer"
},
"homeTeam": {
"type": "string"
},
"id": {
"type": "integer"
},
"isPlayoff": {
"type": "boolean"
},
"lineScores": {
"type": "array",
"items": {
"$ref": "#/definitions/controllers.LineScoreDTO"
}
},
"playerGameAdvStats": {
"type": "array",
"items": {
"$ref": "#/definitions/controllers.PlayerGameAdvStatDTO"
}
},
"playerGameBasicStats": {
"type": "array",
"items": {
"$ref": "#/definitions/controllers.PlayerGameBasicStatDTO"
}
},
"startTimeET": {
"type": "string"
},
"teamGameAdvStats": {
"type": "array",
"items": {
"$ref": "#/definitions/controllers.TeamGameAdvStatDTO"
}
},
"teamGameBasicStats": {
"type": "array",
"items": {
"$ref": "#/definitions/controllers.TeamGameBasicStatDTO"
}
},
"visitorPts": {
"type": "integer"
},
"visitorTeam": {
"type": "string"
}
}
},
"controllers.GamesResponse": {
"type": "object",
"properties": {
"data": {
"type": "array",
"items": {
"$ref": "#/definitions/controllers.GameResponseDTO"
}
},
"pagination": {
"type": "object",
"properties": {
"page": {
"type": "integer"
},
"pageSize": {
"type": "integer"
},
"pages": {
"type": "integer"
},
"total": {
"type": "integer"
}
}
}
}
},
"controllers.LineScoreDTO": {
"type": "object",
"properties": {
"ot1": {
"type": "integer"
},
"ot2": {
"type": "integer"
},
"ot3": {
"type": "integer"
},
"q1": {
"type": "integer"
},
"q2": {
"type": "integer"
},
"q3": {
"type": "integer"
},
"q4": {
"type": "integer"
},
"team": {
"description": "Abbreviation",
"type": "string"
},
"total": {
"type": "integer"
}
}
},
"controllers.PlayerGameAdvStatDTO": {
"type": "object",
"properties": {
"astPercent": {
"type": "number"
},
"blkPercent": {
"type": "number"
},
"bpm": {
"type": "number"
},
"dRtg": {
"type": "integer"
},
"drbPercent": {
"type": "number"
},
"efgPercent": {
"type": "number"
},
"fTr": {
"type": "number"
},
"mp": {
"type": "string"
},
"oRtg": {
"type": "integer"
},
"orbPercent": {
"type": "number"
},
"playerId": {
"type": "string"
},
"playerName": {
"type": "string"
},
"stlPercent": {
"type": "number"
},
"team": {
"description": "Abbreviation",
"type": "string"
},
"threePAr": {
"type": "number"
},
"tovPercent": {
"type": "number"
},
"trbPercent": {
"type": "number"
},
"tsPercent": {
"type": "number"
},
"usgPercent": {
"type": "number"
}
}
},
"controllers.PlayerGameBasicStatDTO": {
"type": "object",
"properties": {
"ast": {
"type": "integer"
},
"blk": {
"type": "integer"
},
"drb": {
"type": "integer"
},
"fg": {
"type": "integer"
},
"fgPercent": {
"type": "number"
},
"fga": {
"type": "integer"
},
"ft": {
"type": "integer"
},
"ftPercent": {
"type": "number"
},
"fta": {
"type": "integer"
},
"gmSc": {
"type": "number"
},
"mp": {
"type": "string"
},
"orb": {
"type": "integer"
},
"pf": {
"type": "integer"
},
"playerId": {
"type": "string"
},
"playerName": {
"type": "string"
},
"plusMinus": {
"type": "integer"
},
"pts": {
"type": "integer"
},
"status": {
"type": "string"
},
"stl": {
"type": "integer"
},
"team": {
"description": "Abbreviation",
"type": "string"
},
"threeP": {
"type": "integer"
},
"threePPercent": {
"type": "number"
},
"threePa": {
"type": "integer"
},
"tov": {
"type": "integer"
},
"trb": {
"type": "integer"
}
}
},
"controllers.TeamGameAdvStatDTO": {
"type": "object",
"properties": {
"astPercent": {
"type": "number"
},
"blkPercent": {
"type": "number"
},
"dRtg": {
"type": "number"
},
"drbPercent": {
"type": "number"
},
"efgPercent": {
"type": "number"
},
"fTr": {
"type": "number"
},
"oRtg": {
"type": "number"
},
"orbPercent": {
"type": "number"
},
"stlPercent": {
"type": "number"
},
"team": {
"description": "Abbreviation",
"type": "string"
},
"threePAr": {
"type": "number"
},
"tovPercent": {
"type": "number"
},
"trbPercent": {
"type": "number"
},
"tsPercent": {
"type": "number"
}
}
},
"controllers.TeamGameBasicStatDTO": {
"type": "object",
"properties": {
"ast": {
"type": "integer"
},
"blk": {
"type": "integer"
},
"drb": {
"type": "integer"
},
"fg": {
"type": "integer"
},
"fgPercent": {
"type": "number"
},
"fga": {
"type": "integer"
},
"ft": {
"type": "integer"
},
"ftPercent": {
"type": "number"
},
"fta": {
"type": "integer"
},
"orb": {
"type": "integer"
},
"pf": {
"type": "integer"
},
"pts": {
"type": "integer"
},
"stl": {
"type": "integer"
},
"team": {
"description": "Abbreviation",
"type": "string"
},
"threeP": {
"type": "integer"
},
"threePPercent": {
"type": "number"
},
"threePa": {
"type": "integer"
},
"tov": {
"type": "integer"
},
"trb": {
"type": "integer"
}
}
},
"models.PlayerAdvancedStat": { "models.PlayerAdvancedStat": {
"type": "object", "type": "object",
"properties": { "properties": {
@@ -460,6 +945,9 @@ const docTemplate = `{
} }
}, },
"tags": [ "tags": [
{
"name": "Games"
},
{ {
"name": "PlayerTotals" "name": "PlayerTotals"
}, },
+488
View File
@@ -11,6 +11,104 @@
}, },
"basePath": "/", "basePath": "/",
"paths": { "paths": {
"/api/games": {
"get": {
"description": "Returns a paginated list of games with optional filters and included associations.",
"produces": [
"application/json"
],
"tags": [
"Games"
],
"summary": "Get game data",
"parameters": [
{
"type": "string",
"description": "Filter by date (YYYY-MM-DD)",
"name": "date",
"in": "query"
},
{
"type": "string",
"description": "Filter by team abbreviation (e.g., LAL)",
"name": "team",
"in": "query"
},
{
"type": "string",
"description": "Filter by a specific Game ID",
"name": "gameId",
"in": "query"
},
{
"type": "boolean",
"description": "Filter for playoff games",
"name": "isPlayoff",
"in": "query"
},
{
"type": "integer",
"default": 1,
"description": "Page number",
"name": "page",
"in": "query"
},
{
"type": "integer",
"default": 20,
"description": "Page size",
"name": "pageSize",
"in": "query"
},
{
"type": "string",
"default": "date",
"description": "Field to sort by (e.g., date)",
"name": "sortBy",
"in": "query"
},
{
"type": "boolean",
"default": false,
"description": "Sort ascending",
"name": "ascending",
"in": "query"
},
{
"type": "string",
"description": "Comma-separated list of associations to include (e.g., lineScores,playerGameBasicStats,teamGameAdvStats)",
"name": "include",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/controllers.GamesResponse"
}
},
"400": {
"description": "Bad Request",
"schema": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
}
}
}
},
"/api/playeradvancedstats": { "/api/playeradvancedstats": {
"get": { "get": {
"description": "Returns filtered and paginated player advanced stats", "description": "Returns filtered and paginated player advanced stats",
@@ -300,6 +398,393 @@
} }
} }
}, },
"controllers.GameResponseDTO": {
"type": "object",
"properties": {
"arena": {
"type": "string"
},
"boxScoreUrl": {
"type": "string"
},
"date": {
"type": "string"
},
"gameDuration": {
"type": "string"
},
"gameId": {
"type": "string"
},
"homePts": {
"type": "integer"
},
"homeTeam": {
"type": "string"
},
"id": {
"type": "integer"
},
"isPlayoff": {
"type": "boolean"
},
"lineScores": {
"type": "array",
"items": {
"$ref": "#/definitions/controllers.LineScoreDTO"
}
},
"playerGameAdvStats": {
"type": "array",
"items": {
"$ref": "#/definitions/controllers.PlayerGameAdvStatDTO"
}
},
"playerGameBasicStats": {
"type": "array",
"items": {
"$ref": "#/definitions/controllers.PlayerGameBasicStatDTO"
}
},
"startTimeET": {
"type": "string"
},
"teamGameAdvStats": {
"type": "array",
"items": {
"$ref": "#/definitions/controllers.TeamGameAdvStatDTO"
}
},
"teamGameBasicStats": {
"type": "array",
"items": {
"$ref": "#/definitions/controllers.TeamGameBasicStatDTO"
}
},
"visitorPts": {
"type": "integer"
},
"visitorTeam": {
"type": "string"
}
}
},
"controllers.GamesResponse": {
"type": "object",
"properties": {
"data": {
"type": "array",
"items": {
"$ref": "#/definitions/controllers.GameResponseDTO"
}
},
"pagination": {
"type": "object",
"properties": {
"page": {
"type": "integer"
},
"pageSize": {
"type": "integer"
},
"pages": {
"type": "integer"
},
"total": {
"type": "integer"
}
}
}
}
},
"controllers.LineScoreDTO": {
"type": "object",
"properties": {
"ot1": {
"type": "integer"
},
"ot2": {
"type": "integer"
},
"ot3": {
"type": "integer"
},
"q1": {
"type": "integer"
},
"q2": {
"type": "integer"
},
"q3": {
"type": "integer"
},
"q4": {
"type": "integer"
},
"team": {
"description": "Abbreviation",
"type": "string"
},
"total": {
"type": "integer"
}
}
},
"controllers.PlayerGameAdvStatDTO": {
"type": "object",
"properties": {
"astPercent": {
"type": "number"
},
"blkPercent": {
"type": "number"
},
"bpm": {
"type": "number"
},
"dRtg": {
"type": "integer"
},
"drbPercent": {
"type": "number"
},
"efgPercent": {
"type": "number"
},
"fTr": {
"type": "number"
},
"mp": {
"type": "string"
},
"oRtg": {
"type": "integer"
},
"orbPercent": {
"type": "number"
},
"playerId": {
"type": "string"
},
"playerName": {
"type": "string"
},
"stlPercent": {
"type": "number"
},
"team": {
"description": "Abbreviation",
"type": "string"
},
"threePAr": {
"type": "number"
},
"tovPercent": {
"type": "number"
},
"trbPercent": {
"type": "number"
},
"tsPercent": {
"type": "number"
},
"usgPercent": {
"type": "number"
}
}
},
"controllers.PlayerGameBasicStatDTO": {
"type": "object",
"properties": {
"ast": {
"type": "integer"
},
"blk": {
"type": "integer"
},
"drb": {
"type": "integer"
},
"fg": {
"type": "integer"
},
"fgPercent": {
"type": "number"
},
"fga": {
"type": "integer"
},
"ft": {
"type": "integer"
},
"ftPercent": {
"type": "number"
},
"fta": {
"type": "integer"
},
"gmSc": {
"type": "number"
},
"mp": {
"type": "string"
},
"orb": {
"type": "integer"
},
"pf": {
"type": "integer"
},
"playerId": {
"type": "string"
},
"playerName": {
"type": "string"
},
"plusMinus": {
"type": "integer"
},
"pts": {
"type": "integer"
},
"status": {
"type": "string"
},
"stl": {
"type": "integer"
},
"team": {
"description": "Abbreviation",
"type": "string"
},
"threeP": {
"type": "integer"
},
"threePPercent": {
"type": "number"
},
"threePa": {
"type": "integer"
},
"tov": {
"type": "integer"
},
"trb": {
"type": "integer"
}
}
},
"controllers.TeamGameAdvStatDTO": {
"type": "object",
"properties": {
"astPercent": {
"type": "number"
},
"blkPercent": {
"type": "number"
},
"dRtg": {
"type": "number"
},
"drbPercent": {
"type": "number"
},
"efgPercent": {
"type": "number"
},
"fTr": {
"type": "number"
},
"oRtg": {
"type": "number"
},
"orbPercent": {
"type": "number"
},
"stlPercent": {
"type": "number"
},
"team": {
"description": "Abbreviation",
"type": "string"
},
"threePAr": {
"type": "number"
},
"tovPercent": {
"type": "number"
},
"trbPercent": {
"type": "number"
},
"tsPercent": {
"type": "number"
}
}
},
"controllers.TeamGameBasicStatDTO": {
"type": "object",
"properties": {
"ast": {
"type": "integer"
},
"blk": {
"type": "integer"
},
"drb": {
"type": "integer"
},
"fg": {
"type": "integer"
},
"fgPercent": {
"type": "number"
},
"fga": {
"type": "integer"
},
"ft": {
"type": "integer"
},
"ftPercent": {
"type": "number"
},
"fta": {
"type": "integer"
},
"orb": {
"type": "integer"
},
"pf": {
"type": "integer"
},
"pts": {
"type": "integer"
},
"stl": {
"type": "integer"
},
"team": {
"description": "Abbreviation",
"type": "string"
},
"threeP": {
"type": "integer"
},
"threePPercent": {
"type": "number"
},
"threePa": {
"type": "integer"
},
"tov": {
"type": "integer"
},
"trb": {
"type": "integer"
}
}
},
"models.PlayerAdvancedStat": { "models.PlayerAdvancedStat": {
"type": "object", "type": "object",
"properties": { "properties": {
@@ -456,6 +941,9 @@
} }
}, },
"tags": [ "tags": [
{
"name": "Games"
},
{ {
"name": "PlayerTotals" "name": "PlayerTotals"
}, },
+325
View File
@@ -18,6 +18,263 @@ definitions:
type: integer type: integer
type: object type: object
type: object type: object
controllers.GameResponseDTO:
properties:
arena:
type: string
boxScoreUrl:
type: string
date:
type: string
gameDuration:
type: string
gameId:
type: string
homePts:
type: integer
homeTeam:
type: string
id:
type: integer
isPlayoff:
type: boolean
lineScores:
items:
$ref: '#/definitions/controllers.LineScoreDTO'
type: array
playerGameAdvStats:
items:
$ref: '#/definitions/controllers.PlayerGameAdvStatDTO'
type: array
playerGameBasicStats:
items:
$ref: '#/definitions/controllers.PlayerGameBasicStatDTO'
type: array
startTimeET:
type: string
teamGameAdvStats:
items:
$ref: '#/definitions/controllers.TeamGameAdvStatDTO'
type: array
teamGameBasicStats:
items:
$ref: '#/definitions/controllers.TeamGameBasicStatDTO'
type: array
visitorPts:
type: integer
visitorTeam:
type: string
type: object
controllers.GamesResponse:
properties:
data:
items:
$ref: '#/definitions/controllers.GameResponseDTO'
type: array
pagination:
properties:
page:
type: integer
pageSize:
type: integer
pages:
type: integer
total:
type: integer
type: object
type: object
controllers.LineScoreDTO:
properties:
ot1:
type: integer
ot2:
type: integer
ot3:
type: integer
q1:
type: integer
q2:
type: integer
q3:
type: integer
q4:
type: integer
team:
description: Abbreviation
type: string
total:
type: integer
type: object
controllers.PlayerGameAdvStatDTO:
properties:
astPercent:
type: number
blkPercent:
type: number
bpm:
type: number
dRtg:
type: integer
drbPercent:
type: number
efgPercent:
type: number
fTr:
type: number
mp:
type: string
oRtg:
type: integer
orbPercent:
type: number
playerId:
type: string
playerName:
type: string
stlPercent:
type: number
team:
description: Abbreviation
type: string
threePAr:
type: number
tovPercent:
type: number
trbPercent:
type: number
tsPercent:
type: number
usgPercent:
type: number
type: object
controllers.PlayerGameBasicStatDTO:
properties:
ast:
type: integer
blk:
type: integer
drb:
type: integer
fg:
type: integer
fgPercent:
type: number
fga:
type: integer
ft:
type: integer
ftPercent:
type: number
fta:
type: integer
gmSc:
type: number
mp:
type: string
orb:
type: integer
pf:
type: integer
playerId:
type: string
playerName:
type: string
plusMinus:
type: integer
pts:
type: integer
status:
type: string
stl:
type: integer
team:
description: Abbreviation
type: string
threeP:
type: integer
threePPercent:
type: number
threePa:
type: integer
tov:
type: integer
trb:
type: integer
type: object
controllers.TeamGameAdvStatDTO:
properties:
astPercent:
type: number
blkPercent:
type: number
dRtg:
type: number
drbPercent:
type: number
efgPercent:
type: number
fTr:
type: number
oRtg:
type: number
orbPercent:
type: number
stlPercent:
type: number
team:
description: Abbreviation
type: string
threePAr:
type: number
tovPercent:
type: number
trbPercent:
type: number
tsPercent:
type: number
type: object
controllers.TeamGameBasicStatDTO:
properties:
ast:
type: integer
blk:
type: integer
drb:
type: integer
fg:
type: integer
fgPercent:
type: number
fga:
type: integer
ft:
type: integer
ftPercent:
type: number
fta:
type: integer
orb:
type: integer
pf:
type: integer
pts:
type: integer
stl:
type: integer
team:
description: Abbreviation
type: string
threeP:
type: integer
threePPercent:
type: number
threePa:
type: integer
tov:
type: integer
trb:
type: integer
type: object
models.PlayerAdvancedStat: models.PlayerAdvancedStat:
properties: properties:
age: age:
@@ -127,6 +384,73 @@ info:
title: NBA_Go API title: NBA_Go API
version: "1.0" version: "1.0"
paths: paths:
/api/games:
get:
description: Returns a paginated list of games with optional filters and included
associations.
parameters:
- description: Filter by date (YYYY-MM-DD)
in: query
name: date
type: string
- description: Filter by team abbreviation (e.g., LAL)
in: query
name: team
type: string
- description: Filter by a specific Game ID
in: query
name: gameId
type: string
- description: Filter for playoff games
in: query
name: isPlayoff
type: boolean
- default: 1
description: Page number
in: query
name: page
type: integer
- default: 20
description: Page size
in: query
name: pageSize
type: integer
- default: date
description: Field to sort by (e.g., date)
in: query
name: sortBy
type: string
- default: false
description: Sort ascending
in: query
name: ascending
type: boolean
- description: Comma-separated list of associations to include (e.g., lineScores,playerGameBasicStats,teamGameAdvStats)
in: query
name: include
type: string
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/controllers.GamesResponse'
"400":
description: Bad Request
schema:
additionalProperties:
type: string
type: object
"500":
description: Internal Server Error
schema:
additionalProperties:
type: string
type: object
summary: Get game data
tags:
- Games
/api/playeradvancedstats: /api/playeradvancedstats:
get: get:
consumes: consumes:
@@ -306,6 +630,7 @@ schemes:
- http - http
swagger: "2.0" swagger: "2.0"
tags: tags:
- name: Games
- name: PlayerTotals - name: PlayerTotals
- name: PlayerStats - name: PlayerStats
- name: PlayerShotChart - name: PlayerShotChart
+2
View File
@@ -5,6 +5,7 @@
// @schemes http // @schemes http
// @BasePath / // @BasePath /
// //
// @tag.name Games
// @tag.name PlayerTotals // @tag.name PlayerTotals
// @tag.name PlayerStats // @tag.name PlayerStats
// @tag.name PlayerShotChart // @tag.name PlayerShotChart
@@ -114,6 +115,7 @@ func main() {
routes.RegisterPlayerAdvancedRoutes(app, db) routes.RegisterPlayerAdvancedRoutes(app, db)
routes.RegisterPlayerTotalRoutes(app, db) routes.RegisterPlayerTotalRoutes(app, db)
routes.RegisterPlayerShotChartRoutes(app, db) routes.RegisterPlayerShotChartRoutes(app, db)
routes.RegisterGameRoutes(app, db)
/* ---------- START & SHUTDOWN ---------- */ /* ---------- START & SHUTDOWN ---------- */
go func() { go func() {
+15
View File
@@ -0,0 +1,15 @@
package routes
import (
"github.com/gofiber/fiber/v2"
"gorm.io/gorm"
"github.com/nprasad2077/NBA_Go/controllers"
)
func RegisterGameRoutes(app *fiber.App, db *gorm.DB) {
// Create a new group for game-related endpoints
api := app.Group("/api/games")
// Register the GET endpoint to fetch games
api.Get("/", controllers.GetGames(db))
}
+64
View File
@@ -0,0 +1,64 @@
// utils/teams.go
package utils
// teamNameToAbbr is un-exported (private to the package) and serves as the single source of truth.
var teamNameToAbbr = map[string]string{
"Atlanta Hawks": "ATL",
"Boston Celtics": "BOS",
"Brooklyn Nets": "BKN",
"Charlotte Hornets": "CHA",
"Chicago Bulls": "CHI",
"Cleveland Cavaliers": "CLE",
"Dallas Mavericks": "DAL",
"Denver Nuggets": "DEN",
"Detroit Pistons": "DET",
"Golden State Warriors": "GSW",
"Houston Rockets": "HOU",
"Indiana Pacers": "IND",
"Los Angeles Clippers": "LAC",
"Los Angeles Lakers": "LAL",
"Memphis Grizzlies": "MEM",
"Miami Heat": "MIA",
"Milwaukee Bucks": "MIL",
"Minnesota Timberwolves":"MIN",
"New Orleans Pelicans": "NOP",
"New York Knicks": "NYK",
"Oklahoma City Thunder": "OKC",
"Orlando Magic": "ORL",
"Philadelphia 76ers": "PHI",
"Phoenix Suns": "PHX",
"Portland Trail Blazers":"POR",
"Sacramento Kings": "SAC",
"San Antonio Spurs": "SAS",
"Toronto Raptors": "TOR",
"Utah Jazz": "UTA",
"Washington Wizards": "WAS",
}
// TeamAbbrToName is the exported reverse map.
var TeamAbbrToName map[string]string
// init() runs automatically when the package is first used.
// It creates the reverse map so it's ready for use.
func init() {
TeamAbbrToName = make(map[string]string, len(teamNameToAbbr))
for name, abbr := range teamNameToAbbr {
TeamAbbrToName[abbr] = name
}
}
// GetAbbreviation looks up a team's full name and returns its abbreviation.
func GetAbbreviation(fullName string) string {
if abbr, ok := teamNameToAbbr[fullName]; ok {
return abbr
}
return fullName // Fallback
}
// GetFullName looks up a team's abbreviation and returns its full name.
func GetFullName(abbr string) string {
if name, ok := TeamAbbrToName[abbr]; ok {
return name
}
return abbr // Fallback
}