Skip to content

Commit

Permalink
classic league implementation is done
Browse files Browse the repository at this point in the history
  • Loading branch information
AbdoAnss committed Dec 14, 2024
1 parent 9a46f46 commit de3c838
Show file tree
Hide file tree
Showing 6 changed files with 356 additions and 34 deletions.
2 changes: 2 additions & 0 deletions client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ type Client struct {
Fixtures *endpoints.FixtureService
Teams *endpoints.TeamService
Managers *endpoints.ManagerService
Leagues *endpoints.LeagueService
}

func NewClient(opts ...Option) *Client {
Expand Down Expand Up @@ -58,6 +59,7 @@ func NewClient(opts ...Option) *Client {
c.Managers = endpoints.NewManagerService(c, c.Bootstrap)
// standalone services
c.Fixtures = endpoints.NewFixtureService(c)
c.Leagues = endpoints.NewLeagueService(c)

return c
}
Expand Down
34 changes: 0 additions & 34 deletions dockerfile

This file was deleted.

1 change: 1 addition & 0 deletions endpoints/bootstrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ var (
gameweeksCacheTTL = 3 * time.Minute // Gameweeks status might change more often
settingsCacheTTL = 24 * time.Hour // Game settings rarely change
managerCacheTTL = 5 * time.Minute // Managers data updates frequently
leagueCacheTTL = 5 * time.Minute // Leagues update frequently
)

func init() {
Expand Down
140 changes: 140 additions & 0 deletions endpoints/leagues.go
Original file line number Diff line number Diff line change
@@ -1 +1,141 @@
package endpoints

import (
"encoding/json"
"fmt"
"io"
"net/http"

"github.com/AbdoAnss/go-fantasy-pl/api"
"github.com/AbdoAnss/go-fantasy-pl/models"
)

const (
classicLeagueEndpoint = "/leagues-classic/%d/standings/?page_standings=%d"
h2hLeagueEndpoint = "/leagues-h2h-matches/league/%d/"
maxPageCache = 3 // Only cache first 3 pages
)

type LeagueService struct {
client api.Client
}

func NewLeagueService(client api.Client) *LeagueService {
return &LeagueService{
client: client,
}
}

func (ls *LeagueService) GetClassicLeagueStandings(id, page int) (*models.ClassicLeague, error) {
// Only cache first few pages to prevent memory bloat
useCache := page <= maxPageCache

if useCache {
cacheKey := fmt.Sprintf("classic_league_%d_page_%d", id, page)
if cached, found := sharedCache.Get(cacheKey); found {
if league, ok := cached.(*models.ClassicLeague); ok {
return league, nil
}
}
}

endpoint := fmt.Sprintf(classicLeagueEndpoint, id, page)
resp, err := ls.client.Get(endpoint)
if err != nil {
return nil, fmt.Errorf("failed to get league standings: %w", err)
}
defer resp.Body.Close()

switch resp.StatusCode {
case http.StatusOK:
case http.StatusNotFound:
return nil, fmt.Errorf("league with ID %d not found", id)
default:
return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
}

body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("failed to read response body: %w", err)
}

var league models.ClassicLeague
if err := json.Unmarshal(body, &league); err != nil {
return nil, fmt.Errorf("failed to decode league data: %w", err)
}

if err := ls.validateLeague(&league); err != nil {
return nil, err
}

if useCache {
cacheKey := fmt.Sprintf("classic_league_%d_page_%d", id, page)
sharedCache.Set(cacheKey, &league, leagueCacheTTL)
}

return &league, nil
}

/*
func (ls *LeagueService) GetH2HLeague(id int) (*models.H2HLeague, error) {
cacheKey := fmt.Sprintf("h2h_league_%d", id)
if cached, found := sharedCache.Get(cacheKey); found {
if league, ok := cached.(*models.H2HLeague); ok {
return league, nil
}
}
endpoint := fmt.Sprintf(h2hLeagueEndpoint, id)
resp, err := ls.client.Get(endpoint)
if err != nil {
return nil, fmt.Errorf("failed to get H2H league: %w", err)
}
defer resp.Body.Close()
switch resp.StatusCode {
case http.StatusOK:
// Continue processing
case http.StatusNotFound:
return nil, fmt.Errorf("H2H league with ID %d not found", id)
default:
return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
}
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("failed to read response body: %w", err)
}
var league models.H2HLeague
if err := json.Unmarshal(body, &league); err != nil {
return nil, fmt.Errorf("failed to decode H2H league data: %w", err)
}
sharedCache.Set(cacheKey, &league, leagueCacheTTL)
return &league, nil
}
*/

func (ls *LeagueService) validateLeague(league *models.ClassicLeague) error {
if league == nil {
return fmt.Errorf("received nil league data")
}
if league.League.ID == 0 {
return fmt.Errorf("invalid league ID")
}
return nil
}

func (ls *LeagueService) GetTotalPages(league *models.ClassicLeague) int {
if league == nil || len(league.Standings.Results) == 0 {
return 0
}

totalEntries := len(league.Standings.Results)
if league.League.GetMaxEntries() > 0 {
totalEntries = league.League.GetMaxEntries()
}

entriesPerPage := 50 // FPL default
return (totalEntries + entriesPerPage - 1) / entriesPerPage
}
88 changes: 88 additions & 0 deletions endpoints/leagues_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package endpoints_test

import (
"testing"

"github.com/AbdoAnss/go-fantasy-pl/client"
"github.com/stretchr/testify/assert"
)

var testLeagueClient *client.Client

func init() {
testLeagueClient = client.NewClient()
}

func TestLeagueEndpoints(t *testing.T) {
t.Run("GetClassicLeague", func(t *testing.T) {
// INPT Fantasy LeagueID
leagueID := 1185652
page := 1

league, err := testLeagueClient.Leagues.GetClassicLeagueStandings(leagueID, page)
assert.NoError(t, err, "expected no error when getting classic league")
assert.NotNil(t, league, "expected league to be returned")

// Log league details
t.Logf("\nLeague Details:")
t.Logf("League: %s", league.GetLeagueInfo())
t.Logf("Created: %s", league.League.GetCreationDate())
t.Logf("Type: %s", league.League.LeagueType)
t.Logf("Last Updated: %s", league.GetUpdateTime())
t.Logf("Max Entries: %d", league.League.GetMaxEntries())

// Log standings
t.Logf("\nTop 4 Managers:")
for _, manager := range league.GetTopManagers(4) {
t.Logf("%s", manager.GetManagerInfo())
t.Logf(" Points: %d (GW: %d)", manager.Total, manager.EventTotal)
t.Logf(" Rank: %d %s", manager.Rank, manager.GetRankChangeString())
}

// Log pagination
t.Logf("\nPagination:")
t.Logf("Current: %s", league.Standings.GetPageInfo())
t.Logf("Has Previous: %v", league.Standings.HasPreviousPage())
t.Logf("Has Next: %v", league.Standings.HasNext)
})

t.Run("ValidateLeagueData", func(t *testing.T) {
leagueID := 1185652
league, err := testLeagueClient.Leagues.GetClassicLeagueStandings(leagueID, 1)
assert.NoError(t, err)

// Validate league structure
assert.Equal(t, leagueID, league.League.ID)
assert.NotEmpty(t, league.League.Name)
assert.NotZero(t, league.League.Created)

// Validate standings
topManagers := league.GetTopManagers(1)
assert.NotEmpty(t, topManagers)
assert.Greater(t, topManagers[0].Entry, 0)
assert.NotEmpty(t, topManagers[0].GetManagerInfo())
assert.GreaterOrEqual(t, topManagers[0].Total, 0)
})

t.Run("GetNonExistentLeague", func(t *testing.T) {
league, err := testLeagueClient.Leagues.GetClassicLeagueStandings(99999999, 1)
assert.Error(t, err)
assert.Nil(t, league)
assert.Contains(t, err.Error(), "not found")
})

t.Run("CacheConsistency", func(t *testing.T) {
leagueID := 1185652

league1, err := testLeagueClient.Leagues.GetClassicLeagueStandings(leagueID, 1)
assert.NoError(t, err)

league2, err := testLeagueClient.Leagues.GetClassicLeagueStandings(leagueID, 1)
assert.NoError(t, err)

assert.Equal(t, league1.GetLeagueInfo(), league2.GetLeagueInfo())
assert.Equal(t,
league1.GetTopManagers(1)[0].GetManagerInfo(),
league2.GetTopManagers(1)[0].GetManagerInfo())
})
}
Loading

0 comments on commit de3c838

Please sign in to comment.