TUI: sync sportsball favorites into sports source on startup
d2c999e6a1ea9a10d88b3e46189c77644c6db0ac
humdrum <me@humdrum.me> · 2026-07-08 16:24
parent 27f29920
1 files changed
tui/dashboard.py +52 −0
@@ -26,6 +26,7 @@ in the terminal (dash accepts a key argument to skip its gum menu).
"""
from __future__ import annotations
+import json
import re
import shlex
import subprocess
@@ -54,6 +55,17 @@ BASE_URL = "http://localhost:4317"
REFRESH_SECS = 30
SYS_REFRESH_SECS = 2.5
REPO = Path(__file__).parent.parent
+
+# sportsball (~/.config/sportsball/config.json) league keys → ESPN sport/league
+# paths used by this dashboard's sports source. Mirrors sportsball's league catalog.
+SPORTSBALL_LEAGUE_PATH = {
+ "worldcup": "soccer/fifa.world",
+ "mlb": "baseball/mlb",
+ "nba": "basketball/nba",
+ "wnba": "basketball/wnba",
+ "nhl": "hockey/nhl",
+ "nfl": "football/nfl",
+}
PAGES = [
("page-personal", "Personal"),
@@ -1079,6 +1091,7 @@
self.refresh_data()
self.set_interval(REFRESH_SECS, self.refresh_data)
self.set_interval(60, self.poll_sports_live)
+ self.sync_favorites()
def _q(self, selector: str, expect_type):
"""query_one against the main screen — app.query_one resolves against
@@ -1217,6 +1230,45 @@ render_sports(p) if p else Text("No games.", style="dim italic")
)
games = (p or {}).get("games", [])
self._sports_live = any(g.get("state") == "in" for g in games)
+
+ @work(exclusive=True, group="favsync")
+ async def sync_favorites(self) -> None:
+ # On app open, mirror sportsball's favorite teams into this dashboard's
+ # sports source config (once — not per fetch). Silently no-op if the file
+ # is absent, has no favorites, or the server is unreachable.
+ cfg = Path.home() / ".config" / "sportsball" / "config.json"
+ try:
+ favs = json.loads(cfg.read_text()).get("favorites") or []
+ except Exception:
+ return
+ teams = []
+ for f in favs:
+ path = SPORTSBALL_LEAGUE_PATH.get(f.get("league"))
+ team = f.get("abbr") or f.get("name")
+ if path and team:
+ teams.append({"league": path, "team": team})
+ if not teams:
+ return
+ try:
+ async with httpx.AsyncClient(timeout=15) as c:
+ r = await c.get(f"{BASE_URL}/api/sources")
+ r.raise_for_status()
+ src = next(
+ (s for s in r.json().get("sources", [])
+ if (s.get("source") or {}).get("kind") == "sports"),
+ None,
+ )
+ if not src:
+ return
+ sid = (src.get("source") or {}).get("id")
+ cur = (src.get("source") or {}).get("config") or {}
+ await c.patch(
+ f"{BASE_URL}/api/sources/{sid}",
+ json={"enabled": True, "config": {**cur, "teams": teams}},
+ )
+ except Exception:
+ return
+ self.refresh_data()
@work(exclusive=True, thread=True, group="sys")
def refresh_system(self) -> None: