| """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) |
| print("Auto-refresh triggered") |
| do_fetch() |
|
|
|
|
| |
| thread = threading.Thread(target=do_fetch, daemon=True) |
| thread.start() |
|
|
| |
| 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"] |
| } |
| |
| 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"] |
| } |
| |
| 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] |
| |
| |
| 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] |
| |
| |
| 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) |