diff options
| author | Christian Cleberg <[email protected]> | 2026-04-15 22:28:45 -0500 |
|---|---|---|
| committer | Christian Cleberg <[email protected]> | 2026-04-15 22:28:45 -0500 |
| commit | 4cbe592e0a0fae13f0a89918a66fd983dd84464f (patch) | |
| tree | c2aac9d6e3c7b0f44adf07b86385f2ce06f1cf10 /nba/leaders.py | |
| parent | 00f695c765aead8aa0160a3c676cf85716178beb (diff) | |
| download | nba-scores-4cbe592e0a0fae13f0a89918a66fd983dd84464f.tar.gz nba-scores-4cbe592e0a0fae13f0a89918a66fd983dd84464f.tar.bz2 nba-scores-4cbe592e0a0fae13f0a89918a66fd983dd84464f.zip | |
feat: add Textual TUI with auto-refresh, leaders, playoff picture, and box score
Introduces an interactive terminal UI via the `--tui` flag backed by the
Textual framework. Scores and standings auto-refresh on a configurable
interval (default 60s, minimum 10s) using asyncio with nba_api calls
offloaded to a thread pool to keep the event loop unblocked.
New tabs:
- Scores / Standings (existing views, now live-updating)
- Leaders: top-25 by stat category, cycle with < / > keys
- Playoff Picture: conference seeding with clinch/elimination status
- Box Score: per-player live stats, load any game with keys 1–9
Refactored scores.py and standings.py to separate rendering from I/O
(get_scoreboard_table, get_east/west_standings_table), keeping the
existing static CLI path fully intact. Added -sc / -st shortcut flags.
Adds textual>=0.89.1,<7.0 dependency, build-system declaration for uv,
and package-data entry for the bundled .tcss stylesheet.
Diffstat (limited to 'nba/leaders.py')
| -rw-r--r-- | nba/leaders.py | 72 |
1 files changed, 72 insertions, 0 deletions
diff --git a/nba/leaders.py b/nba/leaders.py new file mode 100644 index 0000000..aa6f3b4 --- /dev/null +++ b/nba/leaders.py @@ -0,0 +1,72 @@ +""" +Fetches and formats NBA statistical leaders. +""" + +import json + +from tabulate import tabulate +from nba_api.stats.endpoints.leagueleaders import LeagueLeaders + +BOLD = "\033[1m" +END = "\033[0m" + +# Ordered list of (api_abbreviation, display_label) +CATEGORIES = [ + ("PTS", "Points"), + ("REB", "Rebounds"), + ("AST", "Assists"), + ("STL", "Steals"), + ("BLK", "Blocks"), + ("EFF", "Efficiency"), + ("FG_PCT", "FG%"), + ("FT_PCT", "FT%"), + ("FG3_PCT", "3P%"), +] + +# Extra columns to show alongside RANK, PLAYER, TEAM, GP for each category +_EXTRA_COLS = { + "PTS": ["PTS", "FGM", "FGA", "FG_PCT", "FTM", "FTA", "FT_PCT"], + "REB": ["REB", "OREB", "DREB", "GP"], + "AST": ["AST", "TOV", "AST_TOV", "GP"], + "STL": ["STL", "TOV", "GP"], + "BLK": ["BLK", "PF", "GP"], + "EFF": ["EFF", "PTS", "REB", "AST", "GP"], + "FG_PCT": ["FG_PCT", "FGM", "FGA", "PTS"], + "FT_PCT": ["FT_PCT", "FTM", "FTA", "PTS"], + "FG3_PCT":["FG3_PCT", "FG3M", "FG3A", "PTS"], +} + +_DISPLAY_NAMES = { + "FG_PCT": "FG%", "FT_PCT": "FT%", "FG3_PCT": "3P%", + "FG3M": "3PM", "FG3A": "3PA", + "AST_TOV": "AST/TO", +} + + +def fetch_leaders(category: str = "PTS") -> dict: + endpoint = LeagueLeaders( + stat_category_abbreviation=category, + season_type_all_star="Regular Season", + ) + return json.loads(endpoint.get_json()) + + +def get_leaders_table(data: dict, category: str = "PTS") -> str: + result = data["resultSet"] + headers = result["headers"] + rows = result["rowSet"] + + base = ["RANK", "PLAYER", "TEAM", "GP"] + extra = [c for c in _EXTRA_COLS.get(category, [category]) if c not in base] + wanted = base + extra + + idx = {h: i for i, h in enumerate(headers)} + table_data = [ + [row[idx[col]] for col in wanted if col in idx] + for row in rows[:25] + ] + display_headers = [_DISPLAY_NAMES.get(c, c) for c in wanted if c in idx] + + cat_label = dict(CATEGORIES).get(category, category) + title = f"{BOLD}League Leaders — {cat_label}{END}" + return title + "\n" + tabulate(table_data, headers=display_headers, tablefmt="grid") |
