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
104 lines
3.3 KiB
Python
104 lines
3.3 KiB
Python
#!/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()
|