"""NBA Buzz - FastAPI Backend""" from fastapi import FastAPI, BackgroundTasks from fastapi.responses import HTMLResponse import threading import time from datetime import datetime, timezone from bluesky_fetcher import BlueskyFetcher, load_handles_from_csv import player_matcher import database from nba_teams import get_player_team, get_team_abbreviation, get_team_players, get_all_teams, PLAYER_TEAMS app = FastAPI(title="NBA Buzz API") is_fetching = False fetch_status = "Ready" def do_fetch(): global is_fetching, fetch_status if is_fetching: return is_fetching = True try: handles = load_handles_from_csv("bluesky_handles.csv") if handles: fetch_status = f"Fetching {len(handles)} accounts..." bsky = BlueskyFetcher() posts = bsky.fetch_multiple_accounts(handles, posts_per_account=25, delay=0.15) print(f"Fetched {len(posts)} posts") added, mentions = database.process_and_store_posts(posts, player_matcher, get_player_team) print(f"Stored {added} posts, {mentions} mentions") database.set_last_update() database.cleanup_old_data(days=3) fetch_status = f"Updated {datetime.now(timezone.utc).strftime('%H:%M UTC')}" except Exception as e: fetch_status = f"Error: {str(e)[:50]}" print(f"Fetch error: {e}") finally: is_fetching = False def auto_refresh_loop(): """Background thread that refreshes data every 15 minutes.""" while True: time.sleep(900) # 15 minutes print("Auto-refresh triggered") do_fetch() # Auto-fetch on startup thread = threading.Thread(target=do_fetch, daemon=True) thread.start() # Start auto-refresh loop refresh_thread = threading.Thread(target=auto_refresh_loop, daemon=True) refresh_thread.start() @app.get("/api/status") def get_status(): stats = database.get_database_stats() return { "total_posts": stats['total_posts'], "total_mentions": stats['total_mentions'], "unique_players": stats['unique_players'], "last_update": database.get_last_update(), "is_fetching": is_fetching, "fetch_status": fetch_status } @app.get("/api/players") def get_players(hours: int = 24, limit: int = 50): counts = database.get_player_mention_counts(hours=hours, limit=limit) result = [] for i, item in enumerate(counts, 1): team = item.get("team_name") or "" entry = { "rank": i, "player": item["player_name"], "team": team, "team_abbrev": get_team_abbreviation(team), "mentions": item["mention_count"] } # Add latest mention for top 5 if i <= 5: latest = database.get_latest_mention_for_player(item["player_name"], hours=hours) if latest: entry["latest_mention"] = latest result.append(entry) return result @app.get("/api/teams") def get_teams(hours: int = 24, limit: int = 30): counts = database.get_team_mention_counts(hours=hours, limit=limit) result = [] for i, item in enumerate(counts, 1): entry = { "rank": i, "team": item["team_name"], "team_abbrev": get_team_abbreviation(item["team_name"]), "mentions": item["mention_count"] } # Add latest mention for top 5 if i <= 5: latest = database.get_latest_mention_for_team(item["team_name"], hours=hours) if latest: entry["latest_mention"] = latest result.append(entry) return result @app.get("/api/search") def search(q: str): q_lower = q.lower() players = [p for p in PLAYER_TEAMS.keys() if q_lower in p.lower()][:10] teams = [t for t in get_all_teams() if q_lower in t.lower()][:5] return {"players": players, "teams": teams} @app.get("/api/player/{player_name}") def get_player_info(player_name: str): team = get_player_team(player_name) teammates = get_team_players(team)[:10] if team else [] teammates = [t for t in teammates if t != player_name][:5] # Get period stats period_stats = [] for hours, label in [(6, "6h"), (12, "12h"), (24, "24h"), (48, "48h"), (168, "7d")]: counts = database.get_player_mention_counts(hours=hours, limit=100) mentions = 0 rank = None for i, item in enumerate(counts, 1): if item["player_name"] == player_name: mentions = item["mention_count"] rank = i break period_stats.append({"hours": hours, "label": label, "mentions": mentions, "rank": rank}) return { "player": player_name, "team": team, "team_abbrev": get_team_abbreviation(team), "teammates": teammates, "period_stats": period_stats } @app.get("/api/team/{team_name}") def get_team_info(team_name: str): players = get_team_players(team_name)[:15] # Get period stats period_stats = [] for hours, label in [(6, "6h"), (12, "12h"), (24, "24h"), (48, "48h"), (168, "7d")]: counts = database.get_team_mention_counts(hours=hours, limit=30) mentions = 0 rank = None for i, item in enumerate(counts, 1): if item["team_name"] == team_name: mentions = item["mention_count"] rank = i break period_stats.append({"hours": hours, "label": label, "mentions": mentions, "rank": rank}) return { "team": team_name, "team_abbrev": get_team_abbreviation(team_name), "players": players, "period_stats": period_stats } @app.get("/api/mentions/{player_name}") def get_player_mentions(player_name: str, limit: int = 50): return database.get_player_recent_mentions(player_name, limit=limit) @app.get("/api/team-mentions/{team_name}") def get_team_mentions(team_name: str, limit: int = 50): return database.get_team_recent_mentions(team_name, limit=limit) @app.post("/api/refresh") def trigger_refresh(background_tasks: BackgroundTasks): if is_fetching: return {"status": "already_fetching"} background_tasks.add_task(do_fetch) return {"status": "started"} @app.get("/", response_class=HTMLResponse) def serve_index(): return open("index.html", "r").read() @app.get("/player/{player_name}", response_class=HTMLResponse) def serve_player_page(player_name: str): return open("player.html", "r").read() @app.get("/team/{team_name}", response_class=HTMLResponse) def serve_team_page(team_name: str): return open("team.html", "r").read() if __name__ == "__main__": import uvicorn uvicorn.run(app, host="0.0.0.0", port=7860)