Skip to content

Commit

Permalink
team endpoint logic is done
Browse files Browse the repository at this point in the history
  • Loading branch information
AbdoAnss committed Dec 10, 2024
1 parent 4755308 commit ff7d42a
Show file tree
Hide file tree
Showing 7 changed files with 263 additions and 0 deletions.
2 changes: 2 additions & 0 deletions client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ type Client struct {
// services
Players *endpoints.PlayerService
Fixtures *endpoints.FixtureService
Teams *endpoints.TeamService
}

func NewClient(opts ...Option) *Client {
Expand Down Expand Up @@ -48,6 +49,7 @@ func NewClient(opts ...Option) *Client {

c.Players = endpoints.NewPlayerService(c)
c.Fixtures = endpoints.NewFixtureService(c)
c.Teams = endpoints.NewTeamService(c)

return c
}
Expand Down
4 changes: 4 additions & 0 deletions endpoints/fixtures.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ func NewFixtureService(client api.Client) *FixtureService {
}
}

// TODO:
// Centralized Cache with Namespacing:
// Use a single cache instance and differentiate keys using endpoint-specific prefixes.

var fixturesCache = cache.NewCache()

func init() {
Expand Down
4 changes: 4 additions & 0 deletions endpoints/players.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ func NewPlayerService(client api.Client) *PlayerService {
}
}

// TODO:
// Centralized Cache with Namespacing:
// Use a single cache instance and differentiate keys using endpoint-specific prefixes.

var (
defaultCacheTTL = 10 * time.Minute
playersCache = cache.NewCache()
Expand Down
109 changes: 109 additions & 0 deletions endpoints/teams.go
Original file line number Diff line number Diff line change
@@ -1 +1,110 @@
package endpoints

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

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

/*
**
* Team endpoint is useful for matching team IDs with their corresponding details.
* This endpoint provides information about various teams in the league, including attributes such as:
* - code: A unique identifier for the team.
* - id: The team's ID used for referencing.
* - name: The full name of the team.
* - short_name: A shortened version of the team's name.
* - points: The total points accumulated by the team.
* - played: The number of matches played by the team.
* - win, draw, loss: The counts of wins, draws, and losses respectively.
* - strength: A general strength rating of the team.
* - strength_overall_home and strength_overall_away: Strength ratings for home and away matches.
* - strength_attack_home and strength_attack_away: Attack strength ratings for home and away matches.
* - strength_defence_home and strength_defence_away: Defense strength ratings for home and away matches.
* - pulse_id: A unique identifier used in the FPL system for the team.
*
* Upon inspecting the JSON response, it is observed that some attributes (such as points, played, win, draw, and loss)
* are always 0, especially at the beginning of the season or during certain periods. This makes these attributes
* less interesting for analysis, as they do not provide meaningful insights during those times.
* However, strength-related attributes can still offer valuable insights into the team's potential performance.
*
* This endpoint is essential for applications that require team-specific data for analysis,
* fantasy league management, or displaying team information to users.
*/

const (
teamsEndpoint = "/bootstrap-static/"
)

type TeamService struct {
client api.Client
}

func NewTeamService(client api.Client) *TeamService {
return &TeamService{
client: client,
}
}

// TODO:
// Centralized Cache with Namespacing:
// Use a single cache instance and differentiate keys using endpoint-specific prefixes.

var (
teamCacheTTL = 5 * time.Hour // team infos are rarely modified
teamsCache = cache.NewCache()
)

func init() {
teamsCache.StartCleanupTask(5 * time.Minute)
}

func (ts *TeamService) GetAllTeams() ([]models.Team, error) {
if cached, found := teamsCache.Get("teams"); found {
if teams, ok := cached.([]models.Team); ok {
return teams, nil
}
}

resp, err := ts.client.Get(teamsEndpoint)
if err != nil {
return nil, fmt.Errorf("failed to get teams: %w", err)
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
}

var response struct {
Elements []models.Team `json:"teams"`
}

if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
return nil, fmt.Errorf("failed to decode teams: %w", err)
}

teamsCache.Set("teams", response.Elements, teamCacheTTL)

return response.Elements, nil
}

func (ts *TeamService) GetTeam(id int) (*models.Team, error) {
teams, err := ts.GetAllTeams()
if err != nil {
return nil, err
}

for _, t := range teams {
if t.ID == id {
return &t, nil
}
}

return nil, fmt.Errorf("player with ID %d not found", id)
}
63 changes: 63 additions & 0 deletions endpoints/teams_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package endpoints_test

import (
"testing"

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

var teamID int

func setupTeamsTestService() *endpoints.TeamService {
c := client.NewClient()
teamID = 13

return endpoints.NewTeamService(c)
}

func TestGetAllTeams(t *testing.T) {
ts := setupTeamsTestService()
teams, err := ts.GetAllTeams()

assert.NoError(t, err, "expected no error when getting all teams")

assert.NotEmpty(t, teams, "expected teams to be returned from API")

for i, team := range teams {
t.Logf("Team %d: %s, Short name: %s, Code: %d, Points: %d",
i+1,
team.GetFullName(),
team.GetShortName(),
team.Code,
team.Points) // somehow points are always 0

assert.NotEmpty(t, team.Name, "expected team name to be non-empty")
assert.GreaterOrEqual(t, team.Points, 0, "expected team points to be non-negative")

if i >= 3 {
break
}
}
}

func TestGetTeam(t *testing.T) {
ts := setupTeamsTestService()

team, err := ts.GetTeam(teamID)

assert.NoError(t, err, "expected no error when getting team")

assert.NotNil(t, team, "expected team to be returned, got nil")

t.Logf("----------------------------------------")
t.Logf("Team: %s", team.GetShortName())
t.Logf("Team ID: %d", team.ID)
t.Logf("Points: %d", team.Points) // always 0 ?
t.Logf("Wins: %d", team.Win) // always 0 ?
t.Logf("Draws: %d", team.Draw) // always 0 ?
t.Logf("Losses: %d", team.Loss) // always 0 ?
t.Logf("Position: %d", team.Position) // always 0 ?
t.Logf("Strength: %d", team.Strength)
}
47 changes: 47 additions & 0 deletions examples/teams/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package main

import (
"fmt"
"log"

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

func main() {
c := client.NewClient()

teamID := 8 // Example team ID : Everton

// Get team details
fmt.Printf("Getting team details for ID %d...\n", teamID)
team, err := c.Teams.GetTeam(teamID)
if err != nil {
log.Printf("Warning: Could not get team details: %v\n", err)
return
}

fmt.Println("----------------------------------------")
fmt.Printf("Team ID: %d\n", team.ID)
fmt.Printf("Team Name: %s\n", team.GetFullName())
fmt.Printf("Short Name: %s\n", team.GetShortName())
fmt.Printf("Points: %d\n", team.Points)
fmt.Printf("Played: %d\n", team.Played)
fmt.Printf("Wins: %d\n", team.Win)
fmt.Printf("Draws: %d\n", team.Draw)
fmt.Printf("Losses: %d\n", team.Loss)
fmt.Printf("Position: %d\n", team.Position)
fmt.Printf("Strength: %d\n", team.Strength)
fmt.Printf("Win Rate: %.2f%%\n", team.GetWinRate())
fmt.Printf("Draw Rate: %.2f%%\n", team.GetDrawRate())
fmt.Printf("Loss Rate: %.2f%%\n", team.GetLossRate())

// Check if the team is in the top 4
topN := 4
if team.IsTopTeam(topN) {
fmt.Printf("The team is in the top %d.\n", topN)
} else {
fmt.Printf("The team is not in the top %d.\n", topN)
}

fmt.Println("----------------------------------------")
}
34 changes: 34 additions & 0 deletions models/team.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,37 @@ type Team struct {
func (t *Team) GetShortName() string {
return t.ShortName
}

func (t *Team) GetFullName() string {
return t.Name
}

func (t *Team) GetWinRate() float64 {
if t.Played == 0 {
return 0.0
}
return float64(t.Win) / float64(t.Played) * 100
}

func (t *Team) GetDrawRate() float64 {
if t.Played == 0 {
return 0.0
}
return float64(t.Draw) / float64(t.Played) * 100
}

func (t *Team) GetLossRate() float64 {
if t.Played == 0 {
return 0.0
}
return float64(t.Loss) / float64(t.Played) * 100
}

// This can be used to check if a team is top 4 for example.
func (t *Team) IsTopTeam(topN int) bool {
position := t.Position
if position != 0 {
return t.Position <= topN
}
return false
}

0 comments on commit ff7d42a

Please sign in to comment.