feat: implement MCP server and dashboard for football data platform
Add complete Nike football data platform with: - FastMCP server exposing football data tools over HTTP - RapidAPI client for free-api-live-football-data integration - Bootstrap web dashboard with live match/standings views - REST API endpoints for dashboard consumption - Docker support with multi-stage build - Comprehensive README with architecture docs - Minimal .gitignore replacing verbose Python template
This commit is contained in:
103
scripts/pull_tfc.py
Normal file
103
scripts/pull_tfc.py
Normal file
@@ -0,0 +1,103 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Pull Toronto FC squad and fixtures and store in Portia DB.
|
||||
|
||||
Delegates all API calls and DB writes to nike.sync.sync_team_data()
|
||||
so this script stays thin and the real logic lives in one place.
|
||||
"""
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# Make sure the project root is on the path when run directly
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
|
||||
|
||||
from datetime import date
|
||||
|
||||
from nike import config, db
|
||||
from nike.sync import sync_team_data
|
||||
|
||||
|
||||
def main() -> None:
|
||||
if not config.API_FOOTBALL_KEY:
|
||||
print("❌ API_FOOTBALL_KEY not set in .env")
|
||||
sys.exit(1)
|
||||
|
||||
print("=" * 60)
|
||||
print(" NIKE — Toronto FC Data Pull")
|
||||
print(f" API: {config.API_FOOTBALL_BASE}")
|
||||
print("=" * 60)
|
||||
|
||||
db.create_pool()
|
||||
|
||||
try:
|
||||
results = sync_team_data(
|
||||
team_search=config.TFC_SEARCH,
|
||||
seasons=config.SEASONS,
|
||||
)
|
||||
finally:
|
||||
db.close_pool()
|
||||
|
||||
# ── Summary ──────────────────────────────────────────
|
||||
if results["errors"]:
|
||||
print("\n⚠️ Errors encountered:")
|
||||
for e in results["errors"]:
|
||||
print(f" • {e}")
|
||||
|
||||
print(f"\n✅ Team: {results.get('team', 'unknown')}")
|
||||
print(f" Players: {results['players']}")
|
||||
print(f" API calls: {results['api_calls']}")
|
||||
for season, count in results.get("seasons", {}).items():
|
||||
print(f" Fixtures {season}: {count}")
|
||||
|
||||
if results.get("squad"):
|
||||
_print_squad(results["squad"])
|
||||
|
||||
if results.get("all_fixtures"):
|
||||
_print_fixtures(results["all_fixtures"])
|
||||
|
||||
|
||||
def _print_squad(squad: list[dict]) -> None:
|
||||
print("\n" + "=" * 60)
|
||||
print(" SQUAD")
|
||||
print("=" * 60)
|
||||
pos_order = ["Goalkeeper", "Defender", "Midfielder", "Attacker"]
|
||||
by_pos: dict = {}
|
||||
for p in squad:
|
||||
by_pos.setdefault(p.get("position") or "Unknown", []).append(p)
|
||||
|
||||
for pos in pos_order + [k for k in by_pos if k not in pos_order]:
|
||||
if pos not in by_pos:
|
||||
continue
|
||||
print(f"\n {pos.upper()}S:")
|
||||
for p in sorted(by_pos[pos],
|
||||
key=lambda x: x["number"] if isinstance(x.get("number"), int) else 99):
|
||||
print(f" #{str(p.get('number', '-')).rjust(2)} {p['name']}")
|
||||
|
||||
|
||||
def _print_fixtures(fixtures: list[dict]) -> None:
|
||||
print("\n" + "=" * 60)
|
||||
print(" FIXTURES")
|
||||
print("=" * 60)
|
||||
today = date.today().isoformat()
|
||||
|
||||
today_f = [f for f in fixtures if f["date"][:10] == today]
|
||||
completed = [f for f in fixtures if f["status"] in ("FT", "AET", "PEN")]
|
||||
upcoming = [f for f in fixtures if f["status"] in ("NS", "TBD", "PST")]
|
||||
|
||||
if today_f:
|
||||
print("\n 🔴 TODAY:")
|
||||
for f in today_f:
|
||||
score = f" {f['score']}" if f.get("score") else ""
|
||||
print(f" {f['home']}{score} {f['away']} @ {f['venue']} [{f['status']}]")
|
||||
if completed:
|
||||
print("\n RECENT RESULTS (last 5):")
|
||||
for f in completed[-5:]:
|
||||
print(f" {f['date'][:10]} {f['home']} {f['score']} {f['away']}")
|
||||
if upcoming:
|
||||
print("\n UPCOMING (next 5):")
|
||||
for f in sorted(upcoming, key=lambda x: x["date"])[:5]:
|
||||
print(f" {f['date'][:10]} {f['home']} vs {f['away']} [{f.get('round', '')}]")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user