# CLAUDE.md This file provides context for Claude Code when working on this project. ## Project Overview SportsTime is a Django-based sports data pipeline that scrapes game schedules from official sources, normalizes the data, stores it in PostgreSQL, and syncs to CloudKit for iOS app consumption. ## Architecture ``` ┌─────────────────┐ ┌──────────────┐ ┌─────────────┐ ┌──────────┐ │ Data Sources │ ──▶ │ Scrapers │ ──▶ │ PostgreSQL │ ──▶ │ CloudKit │ │ (ESPN, leagues) │ │ (sportstime_ │ │ (Django) │ │ (iOS) │ └─────────────────┘ │ parser) │ └─────────────┘ └──────────┘ └──────────────┘ ``` ## Key Directories - `core/` - Django models: Sport, Team, Stadium, Game, Conference, Division, Aliases - `scraper/` - Scraper orchestration, adapter, job management - `sportstime_parser/` - Standalone scraper library (ESPN, league APIs) - `cloudkit/` - CloudKit sync client and job management - `dashboard/` - Staff dashboard for monitoring and controls - `templates/` - Django templates for dashboard UI ## Data Flow 1. **Scraper runs** (manual or scheduled via Celery Beat) 2. **sportstime_parser** fetches from ESPN/league APIs 3. **Adapter** normalizes data and resolves team/stadium names 4. **Django models** store normalized data with CloudKit sync flags 5. **CloudKit sync** pushes pending records to iCloud ## Models Hierarchy ``` Sport ├── Conference │ └── Division │ └── Team (has TeamAliases) ├── Stadium (has StadiumAliases) └── Game (references Team, Stadium) ``` ## Name Resolution Team and stadium names from scraped data are resolved via: 1. Direct ID match (canonical IDs from scraper) 2. Database aliases (TeamAlias/StadiumAlias with date validity) 3. Direct name/abbreviation match Aliases support validity dates for historical names (e.g., team relocations, stadium naming rights). ## Common Tasks ### Run a scraper ```bash docker-compose exec web python manage.py shell >>> from scraper.tasks import run_scraper_task >>> run_scraper_task.delay(config_id) ``` ### Check scraper status Visit `/dashboard/scraper-status/` or check `ScrapeJob` model. ### Add team/stadium alias Use Django admin at `/admin/core/teamalias/` or `/admin/core/stadiumalias/`. ### Export/Import data All admin models support import/export (JSON, CSV, XLSX) via django-import-export. ### Sync to CloudKit ```bash docker-compose exec web python manage.py shell >>> from cloudkit.tasks import run_cloudkit_sync >>> run_cloudkit_sync.delay(config_id) ``` ## Environment - **Docker Compose** for local development - **PostgreSQL** database - **Redis** for Celery broker - **Celery** for async tasks and scheduled jobs ## Key Files - `sportstime/settings.py` - Django settings - `scraper/engine/adapter.py` - Bridges sportstime_parser to Django - `scraper/engine/db_alias_loader.py` - Database alias resolution - `core/resources.py` - Import/export resource definitions - `docker-compose.yml` - Container orchestration ## Supported Sports | Code | Sport | Season Type | |------|-------|-------------| | nba | NBA Basketball | split (Oct-Jun) | | mlb | MLB Baseball | calendar (Mar-Oct) | | nfl | NFL Football | split (Sep-Feb) | | nhl | NHL Hockey | split (Oct-Jun) | | mls | MLS Soccer | calendar (Feb-Nov) | | wnba | WNBA Basketball | calendar (May-Sep) | | nwsl | NWSL Soccer | calendar (Mar-Nov) | ## Testing ```bash docker-compose exec web pytest ``` ## Useful Commands ```bash # Restart containers docker-compose restart # Rebuild after requirements change docker-compose down && docker-compose up -d --build # View logs docker-compose logs -f web # Django shell docker-compose exec web python manage.py shell # Database shell docker-compose exec db psql -U sportstime -d sportstime ```