-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
263 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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("----------------------------------------") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters