diff --git a/config/database.go b/config/database.go index fd56c7e..5afa8ee 100644 --- a/config/database.go +++ b/config/database.go @@ -1,35 +1,35 @@ package config import ( - "log" - "gorm.io/driver/sqlite" - "gorm.io/gorm" - "github.com/nprasad2077/NBA_Go/models" - "github.com/nprasad2077/NBA_Go/utils/metrics" + "github.com/nprasad2077/NBA_Go/models" + "github.com/nprasad2077/NBA_Go/utils/metrics" + "gorm.io/driver/sqlite" + "gorm.io/gorm" + "log" ) func InitDB(shouldMigrate bool) *gorm.DB { - db, err := gorm.Open(sqlite.Open("/app/data/nba.db"), &gorm.Config{}) - if err != nil { - log.Fatalf("failed to connect database: %v", err) - } - metrics.DBOperationsTotal.WithLabelValues("connect", "database").Inc() + db, err := gorm.Open(sqlite.Open("/app/data/nba.db"), &gorm.Config{}) + if err != nil { + log.Fatalf("failed to connect database: %v", err) + } + metrics.DBOperationsTotal.WithLabelValues("connect", "database").Inc() - if shouldMigrate { - if err := db.AutoMigrate(&models.PlayerAdvancedStat{}); err != nil { - log.Fatalf("migrate PlayerAdvancedStat: %v", err) - } - if err := db.AutoMigrate(&models.PlayerTotalStat{}); err != nil { - log.Fatalf("migrate PlayerTotalStat: %v", err) - } - if err := db.AutoMigrate(&models.PlayerShotChart{}); err != nil { - log.Fatalf("migrate PlayerShotChart: %v", err) - } - if err := db.AutoMigrate(&models.APIKey{}); err != nil { - log.Fatalf("migrate APIKey: %v", err) - } - metrics.DBOperationsTotal.WithLabelValues("migrate", "database").Inc() - } + if shouldMigrate { + if err := db.AutoMigrate(&models.PlayerAdvancedStat{}); err != nil { + log.Fatalf("migrate PlayerAdvancedStat: %v", err) + } + if err := db.AutoMigrate(&models.PlayerTotalStat{}); err != nil { + log.Fatalf("migrate PlayerTotalStat: %v", err) + } + if err := db.AutoMigrate(&models.PlayerShotChart{}); err != nil { + log.Fatalf("migrate PlayerShotChart: %v", err) + } + if err := db.AutoMigrate(&models.APIKey{}); err != nil { + log.Fatalf("migrate APIKey: %v", err) + } + metrics.DBOperationsTotal.WithLabelValues("migrate", "database").Inc() + } - return db -} \ No newline at end of file + return db +} diff --git a/controllers/player_advanced_controller.go b/controllers/player_advanced_controller.go index 26de974..1f97649 100644 --- a/controllers/player_advanced_controller.go +++ b/controllers/player_advanced_controller.go @@ -44,18 +44,18 @@ type AdvancedStatsResponse struct { // @Failure 400,500 {object} map[string]string // @Router /api/playeradvancedstats/scrape [get] func ScrapePlayerAdvancedStats(db *gorm.DB) fiber.Handler { - return func(c *fiber.Ctx) error { - season := c.QueryInt("season", 0) - if season == 0 { - return c.Status(400).JSON(fiber.Map{"error":"season is required"}) - } - isPlayoff := c.QueryBool("isPlayoff", false) + return func(c *fiber.Ctx) error { + season := c.QueryInt("season", 0) + if season == 0 { + return c.Status(400).JSON(fiber.Map{"error": "season is required"}) + } + isPlayoff := c.QueryBool("isPlayoff", false) - if err := services.FetchAndStorePlayerAdvancedScrapedStats(db, season, isPlayoff); err != nil { - return c.Status(500).JSON(fiber.Map{"error": err.Error()}) - } - return c.JSON(fiber.Map{"message":"scrape+store complete"}) - } + if err := services.FetchAndStorePlayerAdvancedScrapedStats(db, season, isPlayoff); err != nil { + return c.Status(500).JSON(fiber.Map{"error": err.Error()}) + } + return c.JSON(fiber.Map{"message": "scrape+store complete"}) + } } // GetAllAdvancedPlayerStats godoc diff --git a/controllers/player_shot_chart_controller.go b/controllers/player_shot_chart_controller.go index 477e5b0..e874c1b 100644 --- a/controllers/player_shot_chart_controller.go +++ b/controllers/player_shot_chart_controller.go @@ -1,10 +1,10 @@ package controllers import ( - "github.com/gofiber/fiber/v2" - "github.com/nprasad2077/NBA_Go/models" - "github.com/nprasad2077/NBA_Go/services" - "gorm.io/gorm" + "github.com/gofiber/fiber/v2" + "github.com/nprasad2077/NBA_Go/models" + "github.com/nprasad2077/NBA_Go/services" + "gorm.io/gorm" ) // FetchPlayerShotChartAPI godoc @@ -32,7 +32,9 @@ import ( // ScrapePlayerShotChart godoc // @Summary Scrape a player's shot-chart from BR website // @Description Scrapes seasons [startSeason…endSeason] for the given playerId -// (playerName is auto-detected). +// +// (playerName is auto-detected). +// // @Tags PlayerShotChart // @Accept json // @Produce json @@ -43,25 +45,25 @@ import ( // @Failure 400,500 {object} map[string]string // @Router /api/playershotchart/scrape [get] func ScrapePlayerShotChart(db *gorm.DB) fiber.Handler { - return func(c *fiber.Ctx) error { - pid := c.Query("playerId") - start := c.QueryInt("startSeason", 0) - end := c.QueryInt("endSeason", 0) - if pid == "" || start == 0 || end == 0 { - return c.Status(400).JSON(fiber.Map{ - "error": "playerId, startSeason and endSeason are required", - }) - } - if start < end { - return c.Status(400).JSON(fiber.Map{ - "error": "startSeason must be >= endSeason", - }) - } - if err := services.FetchAndStoreShotChartScrapedForPlayer(db, pid, start, end); err != nil { - return c.Status(500).JSON(fiber.Map{"error": err.Error()}) - } - return c.JSON(fiber.Map{"message": "Shot chart scraped and saved for " + pid}) - } + return func(c *fiber.Ctx) error { + pid := c.Query("playerId") + start := c.QueryInt("startSeason", 0) + end := c.QueryInt("endSeason", 0) + if pid == "" || start == 0 || end == 0 { + return c.Status(400).JSON(fiber.Map{ + "error": "playerId, startSeason and endSeason are required", + }) + } + if start < end { + return c.Status(400).JSON(fiber.Map{ + "error": "startSeason must be >= endSeason", + }) + } + if err := services.FetchAndStoreShotChartScrapedForPlayer(db, pid, start, end); err != nil { + return c.Status(500).JSON(fiber.Map{"error": err.Error()}) + } + return c.JSON(fiber.Map{"message": "Shot chart scraped and saved for " + pid}) + } } // GetPlayerShotChart godoc @@ -77,21 +79,21 @@ func ScrapePlayerShotChart(db *gorm.DB) fiber.Handler { // @Failure 500 {object} map[string]string // @Router /api/playershotchart [get] func GetPlayerShotChart(db *gorm.DB) fiber.Handler { - return func(c *fiber.Ctx) error { - var shots []models.PlayerShotChart + return func(c *fiber.Ctx) error { + var shots []models.PlayerShotChart - query := db.Model(&models.PlayerShotChart{}) + query := db.Model(&models.PlayerShotChart{}) - if pid := c.Query("playerId"); pid != "" { - query = query.Where("player_id = ?", pid) - } - if s := c.QueryInt("season", 0); s != 0 { - query = query.Where("season = ?", s) - } + if pid := c.Query("playerId"); pid != "" { + query = query.Where("player_id = ?", pid) + } + if s := c.QueryInt("season", 0); s != 0 { + query = query.Where("season = ?", s) + } - if err := query.Find(&shots).Error; err != nil { - return c.Status(500).JSON(fiber.Map{"error": err.Error()}) - } - return c.JSON(shots) - } -} \ No newline at end of file + if err := query.Find(&shots).Error; err != nil { + return c.Status(500).JSON(fiber.Map{"error": err.Error()}) + } + return c.JSON(shots) + } +} diff --git a/controllers/player_total_controller.go b/controllers/player_total_controller.go index 3bbcbc6..5207288 100644 --- a/controllers/player_total_controller.go +++ b/controllers/player_total_controller.go @@ -14,9 +14,9 @@ package controllers import ( "github.com/gofiber/fiber/v2" + "github.com/nprasad2077/NBA_Go/models" "github.com/nprasad2077/NBA_Go/services" "gorm.io/gorm" - "github.com/nprasad2077/NBA_Go/models" ) // func FetchPlayerTotalStats(db *gorm.DB) fiber.Handler { @@ -42,23 +42,22 @@ import ( // @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) - if season == 0 { - return c.Status(400).JSON(fiber.Map{"error":"season is required"}) - } - isPlayoff := c.QueryBool("isPlayoff", false) + return func(c *fiber.Ctx) error { + season := c.QueryInt("season", 0) + if season == 0 { + return c.Status(400).JSON(fiber.Map{"error": "season is required"}) + } + isPlayoff := c.QueryBool("isPlayoff", false) - if err := services.FetchAndStorePlayerTotalScrapedStats(db, season, isPlayoff); err != nil { - return c.Status(500).JSON(fiber.Map{"error": err.Error()}) - } - return c.JSON(fiber.Map{"message":"scrapestore complete"}) - } + if err := services.FetchAndStorePlayerTotalScrapedStats(db, season, isPlayoff); err != nil { + return c.Status(500).JSON(fiber.Map{"error": err.Error()}) + } + return c.JSON(fiber.Map{"message": "scrapestore complete"}) + } } - // GetPlayerTotalStats godoc -// @Security ApiKeyAuth +// @Security ApiKeyAuth // @Summary Get player total stats // @Description Filter and paginate player totals // @Tags PlayerTotals @@ -128,4 +127,4 @@ func GetPlayerTotalStats(db *gorm.DB) fiber.Handler { }, }) } -} \ No newline at end of file +} diff --git a/main_test.go b/main_test.go index 8fb7a21..53368ac 100644 --- a/main_test.go +++ b/main_test.go @@ -76,4 +76,4 @@ func TestGetPlayerAdvancedStats(t *testing.T) { body, _ := io.ReadAll(resp.Body) assert.Contains(t, string(body), tc.wantSubstring, tc.name) } -} \ No newline at end of file +} diff --git a/test/loadtest.go b/test/loadtest.go index c26167e..1a7b590 100644 --- a/test/loadtest.go +++ b/test/loadtest.go @@ -148,4 +148,4 @@ func main() { fmt.Printf("Total Time: %v\n", totalTime) fmt.Printf("Average Response Time: %v\n", avgDuration) fmt.Printf("Requests Per Second: %.2f\n", requestsPerSecond) -} \ No newline at end of file +} diff --git a/utils/sleep.go b/utils/sleep.go index 277bd15..b66e594 100644 --- a/utils/sleep.go +++ b/utils/sleep.go @@ -2,25 +2,25 @@ package utils import ( - "log" - "math/rand" - "time" + "log" + "math/rand" + "time" ) func init() { - // Seed the RNG once when the package is initialized - rand.Seed(time.Now().UnixNano()) + // Seed the RNG once when the package is initialized + rand.Seed(time.Now().UnixNano()) } // SleepWithJitter sleeps for the given base duration plus or minus 25% random jitter. // It also logs the actual sleep duration so you can verify it fired correctly. func SleepWithJitter(base time.Duration) { - half := base / 2 - // Random value in [0, half) - randOffset := time.Duration(rand.Int63n(int64(half))) - // Shift to center jitter around zero: [-half/2, +half/2) - delta := randOffset - half/2 - actual := base + delta - log.Printf("⏱️ Sleeping for %v (base=%v, jitter=%v)", actual, base, delta) - time.Sleep(actual) + half := base / 2 + // Random value in [0, half) + randOffset := time.Duration(rand.Int63n(int64(half))) + // Shift to center jitter around zero: [-half/2, +half/2) + delta := randOffset - half/2 + actual := base + delta + log.Printf("⏱️ Sleeping for %v (base=%v, jitter=%v)", actual, base, delta) + time.Sleep(actual) }