Files
Sportstime/ARCHITECTURE.md
Trey t 9088b46563 Initial commit: SportsTime trip planning app
- Three-scenario planning engine (A: date range, B: selected games, C: directional routes)
- GeographicRouteExplorer with anchor game support for route exploration
- Shared ItineraryBuilder for travel segment calculation
- TravelEstimator for driving time/distance estimation
- SwiftUI views for trip creation and detail display
- CloudKit integration for schedule data
- Python scraping scripts for sports schedules

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 00:46:40 -06:00

329 lines
16 KiB
Markdown

# Sport Travel Planner - Architecture Document
## 1. High-Level Architecture
```
┌─────────────────────────────────────────────────────────────────────────────┐
│ PRESENTATION LAYER │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ HomeView │ │ TripView │ │ScheduleView│ │ SettingsView│ │
│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │
│ │ │ │ │ │
│ ┌──────▼──────┐ ┌──────▼──────┐ ┌──────▼──────┐ ┌──────▼──────┐ │
│ │HomeViewModel│ │TripViewModel│ │ScheduleVM │ │ SettingsVM │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────┐
│ DOMAIN LAYER │
│ ┌─────────────────────┐ ┌─────────────────────┐ ┌─────────────────────┐ │
│ │ TripPlanner │ │ RouteOptimizer │ │ ScheduleMatcher │ │
│ │ (Orchestrator) │ │ (Algorithm) │ │ (Game Finder) │ │
│ └──────────┬──────────┘ └──────────┬──────────┘ └──────────┬──────────┘ │
│ │ │ │ │
│ ┌──────────▼────────────────────────▼────────────────────────▼──────────┐ │
│ │ TripPlanningEngine │ │
│ │ • Constraint Solver • Route Graph • Scoring (CoreML) │ │
│ └───────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────┐
│ DATA LAYER │
│ ┌─────────────────────┐ ┌─────────────────────┐ ┌─────────────────────┐ │
│ │ TripRepository │ │ ScheduleRepository │ │ StadiumRepository │ │
│ │ (SwiftData) │ │ (CloudKit) │ │ (CloudKit) │ │
│ └─────────────────────┘ └─────────────────────┘ └─────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────────────┐│
│ │ CloudKit Sync Manager ││
│ │ • Public Database (Schedules, Stadiums) ││
│ │ • Shared Database (Collaborative Trips - Phase 2) ││
│ └─────────────────────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────────────────────┘
```
## 2. Project Structure
```
SportsTime/
├── Core/
│ ├── Models/
│ │ ├── Domain/ # Pure Swift domain models
│ │ │ ├── Sport.swift
│ │ │ ├── Team.swift
│ │ │ ├── Game.swift
│ │ │ ├── Stadium.swift
│ │ │ ├── Trip.swift
│ │ │ ├── TripStop.swift
│ │ │ ├── TravelSegment.swift
│ │ │ └── TripPreferences.swift
│ │ ├── CloudKit/ # CKRecord-backed models
│ │ │ ├── CKGame.swift
│ │ │ ├── CKStadium.swift
│ │ │ └── CKTeam.swift
│ │ └── Local/ # SwiftData models
│ │ ├── SavedTrip.swift
│ │ ├── UserPreferences.swift
│ │ └── CachedSchedule.swift
│ ├── Services/
│ │ ├── CloudKitService.swift
│ │ ├── LocationService.swift
│ │ ├── DistanceMatrixService.swift
│ │ └── ExportService.swift
│ ├── Utilities/
│ │ └── DateFormatter+Extensions.swift
│ └── Extensions/
│ └── CLLocation+Extensions.swift
├── Features/
│ ├── Home/
│ │ ├── Views/
│ │ │ └── HomeView.swift
│ │ └── ViewModels/
│ │ └── HomeViewModel.swift
│ ├── Trip/
│ │ ├── Views/
│ │ │ ├── TripCreationView.swift
│ │ │ ├── TripDetailView.swift
│ │ │ ├── TripStopCard.swift
│ │ │ └── TripPreferencesForm.swift
│ │ └── ViewModels/
│ │ ├── TripCreationViewModel.swift
│ │ └── TripDetailViewModel.swift
│ ├── Schedule/
│ │ ├── Views/
│ │ │ ├── ScheduleListView.swift
│ │ │ └── GameCard.swift
│ │ └── ViewModels/
│ │ └── ScheduleViewModel.swift
│ └── Settings/
│ ├── Views/
│ │ └── SettingsView.swift
│ └── ViewModels/
│ └── SettingsViewModel.swift
├── Planning/
│ ├── Engine/
│ │ ├── TripPlanningEngine.swift
│ │ ├── RouteOptimizer.swift
│ │ ├── ScheduleMatcher.swift
│ │ └── ConstraintSolver.swift
│ ├── Scoring/
│ │ ├── TripScorer.swift
│ │ └── RoutePreferenceModel.mlmodel
│ └── Models/
│ ├── PlanningRequest.swift
│ ├── PlanningResult.swift
│ └── RouteGraph.swift
├── Export/
│ ├── PDFGenerator.swift
│ ├── TripShareManager.swift
│ └── Templates/
│ └── TripPDFTemplate.swift
└── Resources/
└── Assets.xcassets
```
## 3. Data Flow
### Trip Creation Flow
```
User Input → TripCreationViewModel → PlanningRequest
TripPlanningEngine
┌──────────────────────┼──────────────────────┐
▼ ▼ ▼
ScheduleMatcher RouteOptimizer ConstraintSolver
│ │ │
└──────────────────────┼──────────────────────┘
TripScorer (CoreML)
PlanningResult → Trip
TripDetailView (Display)
SavedTrip (SwiftData persist)
```
## 4. CloudKit Schema
### Public Database Records
| Record Type | Fields |
|-------------|--------|
| `Team` | `id`, `name`, `abbreviation`, `sport`, `city`, `stadiumRef` |
| `Stadium` | `id`, `name`, `city`, `state`, `latitude`, `longitude`, `capacity`, `teamRefs` |
| `Game` | `id`, `homeTeamRef`, `awayTeamRef`, `stadiumRef`, `dateTime`, `sport`, `season` |
| `Sport` | `id`, `name`, `seasonStart`, `seasonEnd`, `iconName` |
### Relationships
- Team → Stadium (reference)
- Game → Team (home, away references)
- Game → Stadium (reference)
## 5. Trip Planning Algorithm
### Step 1: Input Parsing
```
Parse user preferences into PlanningRequest:
- Start/End locations (geocoded)
- Date range
- Must-see games (locked stops)
- Travel mode (drive/fly)
- Constraints (max hours/day, EV, lodging type)
```
### Step 2: Game Discovery
```
FOR each sport in selected sports:
Query games within date range
Filter by geographic relevance (within reasonable detour)
Include must-see games regardless of detour
Score games by:
- Team popularity
- Rivalry factor
- Venue uniqueness
```
### Step 3: Route Graph Construction
```
Build weighted graph:
Nodes = [Start, Stadiums with games, End]
Edges = Travel segments with:
- Distance
- Drive time (or flight availability)
- Scenic score (if scenic preference)
- EV charging availability
```
### Step 4: Constraint Solving
```
Apply hard constraints:
- Must-see games are mandatory nodes
- Max stops OR max duration
- Game times must be reachable
Apply soft constraints (scored):
- Leisure level (rest days)
- Driving hours per day
- Scenic preference
```
### Step 5: Route Optimization
```
IF stops < 8:
Use exact TSP solution (branch and bound)
ELSE:
Use nearest-neighbor heuristic + 2-opt improvement
Respect temporal constraints (game dates)
```
### Step 6: Itinerary Generation
```
FOR each day in trip:
Assign:
- Travel segments
- Game attendance
- Lodging location
- Rest periods (based on leisure level)
- EV charging stops (if applicable)
```
### Step 7: Scoring & Ranking
```
Score final itinerary:
- Game quality score
- Route efficiency score
- Fatigue score (inverse)
- User preference alignment
Return top 3 alternatives if possible
```
## 6. CoreML Strategy
### On-Device Models
| Model | Purpose | Input | Output |
|-------|---------|-------|--------|
| `RoutePreferenceModel` | Score route alternatives | Route features vector | Preference score 0-1 |
| `LeisureBalancer` | Optimize rest distribution | Trip features | Rest day placement |
| `PersonalizationModel` | Learn user preferences | Historical trips | Preference weights |
### Training Approach
1. **Initial Model**: Pre-trained on synthetic trip data
2. **On-Device Learning**: Core ML updatable model
3. **Features**: Trip duration, sports mix, driving hours, scenic detours, game density
### Model Integration
```swift
// Scoring a route option
let scorer = try RoutePreferenceModel()
let input = RoutePreferenceModelInput(
totalDistance: route.distance,
gameCount: route.games.count,
scenicScore: route.scenicRating,
avgDriveHours: route.averageDailyDrive,
leisureRatio: route.restDays / route.totalDays
)
let score = try scorer.prediction(input: input).preferenceScore
```
## 7. SwiftUI Screen Flow
```
┌─────────────────┐
│ Launch/Splash │
└────────┬────────┘
┌─────────────────┐
│ HomeView │◄──────────────────────────────────┐
│ - Saved Trips │ │
│ - Quick Start │ │
│ - Schedule │ │
└────────┬────────┘ │
│ │
▼ │
┌─────────────────┐ ┌─────────────────┐ │
│TripCreationView │────►│ GamePickerSheet │ │
│ - Preferences │ │ (Must-See Games)│ │
│ - Locations │ └─────────────────┘ │
│ - Constraints │ │
└────────┬────────┘ │
│ "Plan Trip" │
▼ │
┌─────────────────┐ │
│ PlanningView │ │
│ (Loading State) │ │
└────────┬────────┘ │
▼ │
┌─────────────────┐ ┌─────────────────┐ │
│ TripDetailView │────►│ ExportSheet │ │
│ - Day-by-Day │ │ - PDF │ │
│ - Map View │ │ - Share │ │
│ - Games List │ │ - Email │ │
│ - Save Trip │ └─────────────────┘ │
└────────┬────────┘ │
│ Save │
▼ │
┌─────────────────┐ │
│ SavedTripsView │───────────────────────────────────┘
└─────────────────┘
```
## 8. Key Dependencies
- **SwiftData**: Local persistence for trips and preferences
- **CloudKit**: Shared schedules and stadium data
- **MapKit**: Route visualization and distance calculations
- **CoreML**: On-device trip scoring and personalization
- **PDFKit**: Trip export functionality
- **CoreLocation**: User location and geocoding