Commit 052cd6852c

052cd6852c73f5fe4096e1391f9e90c426fb94c6

parent: 14d8039e4a

Verified · cmc

cmc <hello@cleberg.net> · 2026-05-04 22:12 UTC

fix: ruff format & remove unused home_title var

Layout: unified · split

build/lib/nba/box_score.py +32 −23
@@ -10,7 +10,21 @@ from nba_api.live.nba.endpoints.boxscore import BoxScore
1010BOLD = "\033[1m"
1111END = "\033[0m"
1212
13_HEADERS = ["Player", "Pos", "Min", "Pts", "Reb", "Ast", "Stl", "Blk", "TO", "FG", "3P", "FT", "+/-"]
13_HEADERS = [
14 "Player",
15 "Pos",
16 "Min",
17 "Pts",
18 "Reb",
19 "Ast",
20 "Stl",
21 "Blk",
22 "TO",
23 "FG",
24 "3P",
25 "FT",
26 "+/-",
27]
1428
1529
1630def fetch_box_score(game_id: str) -> dict:
@@ -36,21 +50,23 @@ def _player_rows(players: list) -> list:
3650 if p.get("status") == "INACTIVE":
3751 continue
3852 s = p.get("statistics", {})
39 rows.append([
40 p.get("name", ""),
41 p.get("position", ""),
42 _parse_minutes(s.get("minutes", "")),
43 s.get("points", 0),
44 s.get("reboundsTotal", 0),
45 s.get("assists", 0),
46 s.get("steals", 0),
47 s.get("blocks", 0),
48 s.get("turnovers", 0),
49 f"{s.get('fieldGoalsMade', 0)}/{s.get('fieldGoalsAttempted', 0)}",
50 f"{s.get('threePointersMade', 0)}/{s.get('threePointersAttempted', 0)}",
51 f"{s.get('freeThrowsMade', 0)}/{s.get('freeThrowsAttempted', 0)}",
52 s.get("plusMinusPoints", 0),
53 ])
53 rows.append(
54 [
55 p.get("name", ""),
56 p.get("position", ""),
57 _parse_minutes(s.get("minutes", "")),
58 s.get("points", 0),
59 s.get("reboundsTotal", 0),
60 s.get("assists", 0),
61 s.get("steals", 0),
62 s.get("blocks", 0),
63 s.get("turnovers", 0),
64 f"{s.get('fieldGoalsMade', 0)}/{s.get('fieldGoalsAttempted', 0)}",
65 f"{s.get('threePointersMade', 0)}/{s.get('threePointersAttempted', 0)}",
66 f"{s.get('freeThrowsMade', 0)}/{s.get('freeThrowsAttempted', 0)}",
67 s.get("plusMinusPoints", 0),
68 ]
69 )
5470 return rows
5571
5672
@@ -60,13 +76,6 @@ def get_box_score_tables(data: dict) -> tuple:
6076 home = game["homeTeam"]
6177 away = game["awayTeam"]
6278
63 home_title = (
64 f"{BOLD}{home['teamCity']} {home['teamName']} "
65 f"{home['score']} — {away['score']} "
66 f"{away['teamCity']} {away['teamName']}{END} "
67 f" [{game.get('gameStatusText', '')}]"
68 )
69
7079 home_rows = _player_rows(home.get("players", []))
7180 away_rows = _player_rows(away.get("players", []))
7281
build/lib/nba/cli.py +4 −4
@@ -19,9 +19,7 @@ def nba() -> None:
1919 parser.add_argument(
2020 "--standings", "-st", action="store_true", help="Display the standings"
2121 )
22 parser.add_argument(
23 "--tui", action="store_true", help="Launch the interactive TUI"
24 )
22 parser.add_argument("--tui", action="store_true", help="Launch the interactive TUI")
2523 parser.add_argument(
2624 "--refresh",
2725 type=int,
@@ -47,4 +45,6 @@ def nba() -> None:
4745 elif args.standings:
4846 standings.build_standings(ranks)
4947 else:
50 print("Please specify --scores or --standings (or use --tui for interactive mode)")
48 print(
49 "Please specify --scores or --standings (or use --tui for interactive mode)"
50 )
build/lib/nba/leaders.py +13 −13
@@ -25,20 +25,23 @@ CATEGORIES = [
2525
2626# Extra columns to show alongside RANK, PLAYER, TEAM, GP for each category
2727_EXTRA_COLS = {
28 "PTS": ["PTS", "FGM", "FGA", "FG_PCT", "FTM", "FTA", "FT_PCT"],
29 "REB": ["REB", "OREB", "DREB", "GP"],
30 "AST": ["AST", "TOV", "AST_TOV", "GP"],
31 "STL": ["STL", "TOV", "GP"],
32 "BLK": ["BLK", "PF", "GP"],
33 "EFF": ["EFF", "PTS", "REB", "AST", "GP"],
28 "PTS": ["PTS", "FGM", "FGA", "FG_PCT", "FTM", "FTA", "FT_PCT"],
29 "REB": ["REB", "OREB", "DREB", "GP"],
30 "AST": ["AST", "TOV", "AST_TOV", "GP"],
31 "STL": ["STL", "TOV", "GP"],
32 "BLK": ["BLK", "PF", "GP"],
33 "EFF": ["EFF", "PTS", "REB", "AST", "GP"],
3434 "FG_PCT": ["FG_PCT", "FGM", "FGA", "PTS"],
3535 "FT_PCT": ["FT_PCT", "FTM", "FTA", "PTS"],
36 "FG3_PCT":["FG3_PCT", "FG3M", "FG3A", "PTS"],
36 "FG3_PCT": ["FG3_PCT", "FG3M", "FG3A", "PTS"],
3737}
3838
3939_DISPLAY_NAMES = {
40 "FG_PCT": "FG%", "FT_PCT": "FT%", "FG3_PCT": "3P%",
41 "FG3M": "3PM", "FG3A": "3PA",
40 "FG_PCT": "FG%",
41 "FT_PCT": "FT%",
42 "FG3_PCT": "3P%",
43 "FG3M": "3PM",
44 "FG3A": "3PA",
4245 "AST_TOV": "AST/TO",
4346}
4447
@@ -61,10 +64,7 @@ def get_leaders_table(data: dict, category: str = "PTS") -> str:
6164 wanted = base + extra
6265
6366 idx = {h: i for i, h in enumerate(headers)}
64 table_data = [
65 [row[idx[col]] for col in wanted if col in idx]
66 for row in rows[:25]
67 ]
67 table_data = [[row[idx[col]] for col in wanted if col in idx] for row in rows[:25]]
6868 display_headers = [_DISPLAY_NAMES.get(c, c) for c in wanted if c in idx]
6969
7070 cat_label = dict(CATEGORIES).get(category, category)
build/lib/nba/playoff.py +26 −13
@@ -21,6 +21,7 @@ def fetch_playoff_picture() -> dict:
2121
2222def _clinch_status(row: list, idx: dict) -> str:
2323 """Return a color-coded status string from clinch/elimination columns."""
24
2425 def val(col):
2526 return row[idx[col]] if col in idx else None
2627
@@ -54,19 +55,31 @@ def _build_conference_table(result_sets: list, name: str) -> str:
5455 losses = get(row, "LOSSES")
5556 pct = get(row, "PCT")
5657 pct_str = f"{float(pct):.3f}" if pct not in ("", None) else ""
57 table_data.append([
58 get(row, "RANK"),
59 get(row, "TEAM"),
60 f"{wins}-{losses}",
61 pct_str,
62 get(row, "GB"),
63 get(row, "HOME"),
64 get(row, "AWAY"),
65 get(row, "CONF"),
66 _clinch_status(row, idx),
67 ])
68
69 display_headers = ["#", "Team", "W-L", "PCT", "GB", "HOME", "AWAY", "CONF", "Status"]
58 table_data.append(
59 [
60 get(row, "RANK"),
61 get(row, "TEAM"),
62 f"{wins}-{losses}",
63 pct_str,
64 get(row, "GB"),
65 get(row, "HOME"),
66 get(row, "AWAY"),
67 get(row, "CONF"),
68 _clinch_status(row, idx),
69 ]
70 )
71
72 display_headers = [
73 "#",
74 "Team",
75 "W-L",
76 "PCT",
77 "GB",
78 "HOME",
79 "AWAY",
80 "CONF",
81 "Status",
82 ]
7083 conf_label = "Eastern" if "East" in name else "Western"
7184 title = f"{BOLD}{conf_label} Conference Playoff Picture:{END}"
7285 return title + "\n" + tabulate(table_data, headers=display_headers, tablefmt="grid")
build/lib/nba/standings.py +10 −5
@@ -27,7 +27,9 @@ def _build_conference_table(standings, conference: str) -> str:
2727 streak = team[35]
2828
2929 strk_color = (
30 f"{RED}{streak}{END}" if int(streak) < 0 else f"{GREEN}{streak}{END}"
30 f"{RED}{streak}{END}"
31 if int(streak) < 0
32 else f"{GREEN}{streak}{END}"
3133 )
3234
3335 data.append(
@@ -47,9 +49,8 @@ def _build_conference_table(standings, conference: str) -> str:
4749
4850 headers = ["Rank", "Team", "W-L", "PCT", "GB", "STRK", "L10", "HOME", "AWAY"]
4951 label = "Eastern" if conference == "East" else "Western"
50 return (
51 f"{BOLD}{label} Conference Standings:{END}\n"
52 + tabulate(data, headers=headers, tablefmt="grid")
52 return f"{BOLD}{label} Conference Standings:{END}\n" + tabulate(
53 data, headers=headers, tablefmt="grid"
5354 )
5455
5556
@@ -73,7 +74,11 @@ def get_standings_tables(standings) -> str:
7374 Returns:
7475 str: Formatted standings string with ANSI color codes for both conferences.
7576 """
76 return get_east_standings_table(standings) + "\n\n" + get_west_standings_table(standings)
77 return (
78 get_east_standings_table(standings)
79 + "\n\n"
80 + get_west_standings_table(standings)
81 )
7782
7883
7984def build_standings(standings) -> None:
build/lib/nba/tui/app.py +15 −5
@@ -182,11 +182,15 @@ class NBAApp(App):
182182 self.query_one(TabbedContent).active = "boxscore"
183183
184184 async def action_prev_category(self) -> None:
185 self._leaders_cat_idx = (self._leaders_cat_idx - 1) % len(leaders_mod.CATEGORIES)
185 self._leaders_cat_idx = (self._leaders_cat_idx - 1) % len(
186 leaders_mod.CATEGORIES
187 )
186188 await self._refresh_leaders()
187189
188190 async def action_next_category(self) -> None:
189 self._leaders_cat_idx = (self._leaders_cat_idx + 1) % len(leaders_mod.CATEGORIES)
191 self._leaders_cat_idx = (self._leaders_cat_idx + 1) % len(
192 leaders_mod.CATEGORIES
193 )
190194 await self._refresh_leaders()
191195
192196 # ------------------------------------------------------------------ #
@@ -198,13 +202,17 @@ class NBAApp(App):
198202 self.query_one("#leaders-content", Static).update(f"Loading {label} leaders...")
199203 loop = asyncio.get_event_loop()
200204 try:
201 data = await loop.run_in_executor(None, lambda: leaders_mod.fetch_leaders(cat))
205 data = await loop.run_in_executor(
206 None, lambda: leaders_mod.fetch_leaders(cat)
207 )
202208 self._leaders_data = data
203209 self.query_one("#leaders-content", Static).update(
204210 Text.from_ansi(leaders_mod.get_leaders_table(data, cat))
205211 )
206212 except Exception as exc:
207 self.query_one("#leaders-content", Static).update(f"Error loading leaders: {exc}")
213 self.query_one("#leaders-content", Static).update(
214 f"Error loading leaders: {exc}"
215 )
208216
209217 async def _load_box_score(self, game_idx: int) -> None:
210218 if not self._games:
@@ -233,5 +241,7 @@ class NBAApp(App):
233241 self.query_one("#home-content", Static).update(Text.from_ansi(home_table))
234242 self.query_one("#away-content", Static).update(Text.from_ansi(away_table))
235243 except Exception as exc:
236 self.query_one("#home-content", Static).update(f"Error loading box score: {exc}")
244 self.query_one("#home-content", Static).update(
245 f"Error loading box score: {exc}"
246 )
237247 self.query_one("#away-content", Static).update("")
nba/box_score.py +32 −23
@@ -10,7 +10,21 @@ from nba_api.live.nba.endpoints.boxscore import BoxScore
1010BOLD = "\033[1m"
1111END = "\033[0m"
1212
13_HEADERS = ["Player", "Pos", "Min", "Pts", "Reb", "Ast", "Stl", "Blk", "TO", "FG", "3P", "FT", "+/-"]
13_HEADERS = [
14 "Player",
15 "Pos",
16 "Min",
17 "Pts",
18 "Reb",
19 "Ast",
20 "Stl",
21 "Blk",
22 "TO",
23 "FG",
24 "3P",
25 "FT",
26 "+/-",
27]
1428
1529
1630def fetch_box_score(game_id: str) -> dict:
@@ -36,21 +50,23 @@ def _player_rows(players: list) -> list:
3650 if p.get("status") == "INACTIVE":
3751 continue
3852 s = p.get("statistics", {})
39 rows.append([
40 p.get("name", ""),
41 p.get("position", ""),
42 _parse_minutes(s.get("minutes", "")),
43 s.get("points", 0),
44 s.get("reboundsTotal", 0),
45 s.get("assists", 0),
46 s.get("steals", 0),
47 s.get("blocks", 0),
48 s.get("turnovers", 0),
49 f"{s.get('fieldGoalsMade', 0)}/{s.get('fieldGoalsAttempted', 0)}",
50 f"{s.get('threePointersMade', 0)}/{s.get('threePointersAttempted', 0)}",
51 f"{s.get('freeThrowsMade', 0)}/{s.get('freeThrowsAttempted', 0)}",
52 s.get("plusMinusPoints", 0),
53 ])
53 rows.append(
54 [
55 p.get("name", ""),
56 p.get("position", ""),
57 _parse_minutes(s.get("minutes", "")),
58 s.get("points", 0),
59 s.get("reboundsTotal", 0),
60 s.get("assists", 0),
61 s.get("steals", 0),
62 s.get("blocks", 0),
63 s.get("turnovers", 0),
64 f"{s.get('fieldGoalsMade', 0)}/{s.get('fieldGoalsAttempted', 0)}",
65 f"{s.get('threePointersMade', 0)}/{s.get('threePointersAttempted', 0)}",
66 f"{s.get('freeThrowsMade', 0)}/{s.get('freeThrowsAttempted', 0)}",
67 s.get("plusMinusPoints", 0),
68 ]
69 )
5470 return rows
5571
5672
@@ -60,13 +76,6 @@ def get_box_score_tables(data: dict) -> tuple:
6076 home = game["homeTeam"]
6177 away = game["awayTeam"]
6278
63 home_title = (
64 f"{BOLD}{home['teamCity']} {home['teamName']} "
65 f"{home['score']} — {away['score']} "
66 f"{away['teamCity']} {away['teamName']}{END} "
67 f" [{game.get('gameStatusText', '')}]"
68 )
69
7079 home_rows = _player_rows(home.get("players", []))
7180 away_rows = _player_rows(away.get("players", []))
7281
nba/cli.py +4 −4
@@ -19,9 +19,7 @@ def nba() -> None:
1919 parser.add_argument(
2020 "--standings", "-st", action="store_true", help="Display the standings"
2121 )
22 parser.add_argument(
23 "--tui", action="store_true", help="Launch the interactive TUI"
24 )
22 parser.add_argument("--tui", action="store_true", help="Launch the interactive TUI")
2523 parser.add_argument(
2624 "--refresh",
2725 type=int,
@@ -47,4 +45,6 @@ def nba() -> None:
4745 elif args.standings:
4846 standings.build_standings(ranks)
4947 else:
50 print("Please specify --scores or --standings (or use --tui for interactive mode)")
48 print(
49 "Please specify --scores or --standings (or use --tui for interactive mode)"
50 )
nba/leaders.py +13 −13
@@ -25,20 +25,23 @@ CATEGORIES = [
2525
2626# Extra columns to show alongside RANK, PLAYER, TEAM, GP for each category
2727_EXTRA_COLS = {
28 "PTS": ["PTS", "FGM", "FGA", "FG_PCT", "FTM", "FTA", "FT_PCT"],
29 "REB": ["REB", "OREB", "DREB", "GP"],
30 "AST": ["AST", "TOV", "AST_TOV", "GP"],
31 "STL": ["STL", "TOV", "GP"],
32 "BLK": ["BLK", "PF", "GP"],
33 "EFF": ["EFF", "PTS", "REB", "AST", "GP"],
28 "PTS": ["PTS", "FGM", "FGA", "FG_PCT", "FTM", "FTA", "FT_PCT"],
29 "REB": ["REB", "OREB", "DREB", "GP"],
30 "AST": ["AST", "TOV", "AST_TOV", "GP"],
31 "STL": ["STL", "TOV", "GP"],
32 "BLK": ["BLK", "PF", "GP"],
33 "EFF": ["EFF", "PTS", "REB", "AST", "GP"],
3434 "FG_PCT": ["FG_PCT", "FGM", "FGA", "PTS"],
3535 "FT_PCT": ["FT_PCT", "FTM", "FTA", "PTS"],
36 "FG3_PCT":["FG3_PCT", "FG3M", "FG3A", "PTS"],
36 "FG3_PCT": ["FG3_PCT", "FG3M", "FG3A", "PTS"],
3737}
3838
3939_DISPLAY_NAMES = {
40 "FG_PCT": "FG%", "FT_PCT": "FT%", "FG3_PCT": "3P%",
41 "FG3M": "3PM", "FG3A": "3PA",
40 "FG_PCT": "FG%",
41 "FT_PCT": "FT%",
42 "FG3_PCT": "3P%",
43 "FG3M": "3PM",
44 "FG3A": "3PA",
4245 "AST_TOV": "AST/TO",
4346}
4447
@@ -61,10 +64,7 @@ def get_leaders_table(data: dict, category: str = "PTS") -> str:
6164 wanted = base + extra
6265
6366 idx = {h: i for i, h in enumerate(headers)}
64 table_data = [
65 [row[idx[col]] for col in wanted if col in idx]
66 for row in rows[:25]
67 ]
67 table_data = [[row[idx[col]] for col in wanted if col in idx] for row in rows[:25]]
6868 display_headers = [_DISPLAY_NAMES.get(c, c) for c in wanted if c in idx]
6969
7070 cat_label = dict(CATEGORIES).get(category, category)
nba/playoff.py +26 −13
@@ -21,6 +21,7 @@ def fetch_playoff_picture() -> dict:
2121
2222def _clinch_status(row: list, idx: dict) -> str:
2323 """Return a color-coded status string from clinch/elimination columns."""
24
2425 def val(col):
2526 return row[idx[col]] if col in idx else None
2627
@@ -54,19 +55,31 @@ def _build_conference_table(result_sets: list, name: str) -> str:
5455 losses = get(row, "LOSSES")
5556 pct = get(row, "PCT")
5657 pct_str = f"{float(pct):.3f}" if pct not in ("", None) else ""
57 table_data.append([
58 get(row, "RANK"),
59 get(row, "TEAM"),
60 f"{wins}-{losses}",
61 pct_str,
62 get(row, "GB"),
63 get(row, "HOME"),
64 get(row, "AWAY"),
65 get(row, "CONF"),
66 _clinch_status(row, idx),
67 ])
68
69 display_headers = ["#", "Team", "W-L", "PCT", "GB", "HOME", "AWAY", "CONF", "Status"]
58 table_data.append(
59 [
60 get(row, "RANK"),
61 get(row, "TEAM"),
62 f"{wins}-{losses}",
63 pct_str,
64 get(row, "GB"),
65 get(row, "HOME"),
66 get(row, "AWAY"),
67 get(row, "CONF"),
68 _clinch_status(row, idx),
69 ]
70 )
71
72 display_headers = [
73 "#",
74 "Team",
75 "W-L",
76 "PCT",
77 "GB",
78 "HOME",
79 "AWAY",
80 "CONF",
81 "Status",
82 ]
7083 conf_label = "Eastern" if "East" in name else "Western"
7184 title = f"{BOLD}{conf_label} Conference Playoff Picture:{END}"
7285 return title + "\n" + tabulate(table_data, headers=display_headers, tablefmt="grid")
nba/standings.py +10 −5
@@ -27,7 +27,9 @@ def _build_conference_table(standings, conference: str) -> str:
2727 streak = team[35]
2828
2929 strk_color = (
30 f"{RED}{streak}{END}" if int(streak) < 0 else f"{GREEN}{streak}{END}"
30 f"{RED}{streak}{END}"
31 if int(streak) < 0
32 else f"{GREEN}{streak}{END}"
3133 )
3234
3335 data.append(
@@ -47,9 +49,8 @@ def _build_conference_table(standings, conference: str) -> str:
4749
4850 headers = ["Rank", "Team", "W-L", "PCT", "GB", "STRK", "L10", "HOME", "AWAY"]
4951 label = "Eastern" if conference == "East" else "Western"
50 return (
51 f"{BOLD}{label} Conference Standings:{END}\n"
52 + tabulate(data, headers=headers, tablefmt="grid")
52 return f"{BOLD}{label} Conference Standings:{END}\n" + tabulate(
53 data, headers=headers, tablefmt="grid"
5354 )
5455
5556
@@ -73,7 +74,11 @@ def get_standings_tables(standings) -> str:
7374 Returns:
7475 str: Formatted standings string with ANSI color codes for both conferences.
7576 """
76 return get_east_standings_table(standings) + "\n\n" + get_west_standings_table(standings)
77 return (
78 get_east_standings_table(standings)
79 + "\n\n"
80 + get_west_standings_table(standings)
81 )
7782
7883
7984def build_standings(standings) -> None:
nba/tui/app.py +15 −5
@@ -182,11 +182,15 @@ class NBAApp(App):
182182 self.query_one(TabbedContent).active = "boxscore"
183183
184184 async def action_prev_category(self) -> None:
185 self._leaders_cat_idx = (self._leaders_cat_idx - 1) % len(leaders_mod.CATEGORIES)
185 self._leaders_cat_idx = (self._leaders_cat_idx - 1) % len(
186 leaders_mod.CATEGORIES
187 )
186188 await self._refresh_leaders()
187189
188190 async def action_next_category(self) -> None:
189 self._leaders_cat_idx = (self._leaders_cat_idx + 1) % len(leaders_mod.CATEGORIES)
191 self._leaders_cat_idx = (self._leaders_cat_idx + 1) % len(
192 leaders_mod.CATEGORIES
193 )
190194 await self._refresh_leaders()
191195
192196 # ------------------------------------------------------------------ #
@@ -198,13 +202,17 @@ class NBAApp(App):
198202 self.query_one("#leaders-content", Static).update(f"Loading {label} leaders...")
199203 loop = asyncio.get_event_loop()
200204 try:
201 data = await loop.run_in_executor(None, lambda: leaders_mod.fetch_leaders(cat))
205 data = await loop.run_in_executor(
206 None, lambda: leaders_mod.fetch_leaders(cat)
207 )
202208 self._leaders_data = data
203209 self.query_one("#leaders-content", Static).update(
204210 Text.from_ansi(leaders_mod.get_leaders_table(data, cat))
205211 )
206212 except Exception as exc:
207 self.query_one("#leaders-content", Static).update(f"Error loading leaders: {exc}")
213 self.query_one("#leaders-content", Static).update(
214 f"Error loading leaders: {exc}"
215 )
208216
209217 async def _load_box_score(self, game_idx: int) -> None:
210218 if not self._games:
@@ -233,5 +241,7 @@ class NBAApp(App):
233241 self.query_one("#home-content", Static).update(Text.from_ansi(home_table))
234242 self.query_one("#away-content", Static).update(Text.from_ansi(away_table))
235243 except Exception as exc:
236 self.query_one("#home-content", Static).update(f"Error loading box score: {exc}")
244 self.query_one("#home-content", Static).update(
245 f"Error loading box score: {exc}"
246 )
237247 self.query_one("#away-content", Static).update("")