Add implementation code for all 4 improvement plan phases
Production changes: - TravelEstimator: remove 300mi fallback, return nil on missing coords - TripPlanningEngine: add warnings array, empty sports warning, inverted date range rejection, must-stop filter, segment validation gate - GameDAGRouter: add routePreference parameter with preference-aware bucket ordering and sorting in selectDiverseRoutes() - ScenarioA-E: pass routePreference through to GameDAGRouter - ScenarioA: track games with missing stadium data - ScenarioE: add region filtering for home games - TravelSegment: add requiresOvernightStop and travelDays() helpers Test changes: - GameDAGRouterTests: +252 lines for route preference verification - TripPlanningEngineTests: +153 lines for segment validation, date range, empty sports - ScenarioEPlannerTests: +119 lines for region filter tests - TravelEstimatorTests: remove obsolete fallback distance tests - ItineraryBuilderTests: update nil-coords test expectation Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -81,14 +81,21 @@ final class ScenarioAPlanner: ScenarioPlanner {
|
||||
// Get all games that fall within the user's travel dates.
|
||||
// Sort by start time so we visit them in chronological order.
|
||||
let selectedRegions = request.preferences.selectedRegions
|
||||
var gamesWithMissingStadium = 0
|
||||
let gamesInRange = request.allGames
|
||||
.filter { game in
|
||||
// Must be in date range
|
||||
guard dateRange.contains(game.startTime) else { return false }
|
||||
|
||||
// Track games with missing stadium data
|
||||
guard request.stadiums[game.stadiumId] != nil else {
|
||||
gamesWithMissingStadium += 1
|
||||
return false
|
||||
}
|
||||
|
||||
// Must be in selected region (if regions specified)
|
||||
if !selectedRegions.isEmpty {
|
||||
guard let stadium = request.stadiums[game.stadiumId] else { return false }
|
||||
let stadium = request.stadiums[game.stadiumId]!
|
||||
let gameRegion = Region.classify(longitude: stadium.coordinate.longitude)
|
||||
return selectedRegions.contains(gameRegion)
|
||||
}
|
||||
@@ -98,10 +105,18 @@ final class ScenarioAPlanner: ScenarioPlanner {
|
||||
|
||||
// No games? Nothing to plan.
|
||||
if gamesInRange.isEmpty {
|
||||
var violations: [ConstraintViolation] = []
|
||||
if gamesWithMissingStadium > 0 {
|
||||
violations.append(ConstraintViolation(
|
||||
type: .missingData,
|
||||
description: "\(gamesWithMissingStadium) game(s) excluded due to missing stadium data",
|
||||
severity: .warning
|
||||
))
|
||||
}
|
||||
return .failure(
|
||||
PlanningFailure(
|
||||
reason: .noGamesInRange,
|
||||
violations: []
|
||||
violations: violations
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -165,6 +180,7 @@ final class ScenarioAPlanner: ScenarioPlanner {
|
||||
from: filteredGames,
|
||||
stadiums: request.stadiums,
|
||||
allowRepeatCities: request.preferences.allowRepeatCities,
|
||||
routePreference: request.preferences.routePreference,
|
||||
stopBuilder: buildStops
|
||||
)
|
||||
validRoutes.append(contentsOf: globalRoutes)
|
||||
@@ -173,7 +189,8 @@ final class ScenarioAPlanner: ScenarioPlanner {
|
||||
let regionalRoutes = findRoutesPerRegion(
|
||||
games: filteredGames,
|
||||
stadiums: request.stadiums,
|
||||
allowRepeatCities: request.preferences.allowRepeatCities
|
||||
allowRepeatCities: request.preferences.allowRepeatCities,
|
||||
routePreference: request.preferences.routePreference
|
||||
)
|
||||
validRoutes.append(contentsOf: regionalRoutes)
|
||||
|
||||
@@ -478,7 +495,8 @@ final class ScenarioAPlanner: ScenarioPlanner {
|
||||
private func findRoutesPerRegion(
|
||||
games: [Game],
|
||||
stadiums: [String: Stadium],
|
||||
allowRepeatCities: Bool
|
||||
allowRepeatCities: Bool,
|
||||
routePreference: RoutePreference = .balanced
|
||||
) -> [[Game]] {
|
||||
// Partition games by region
|
||||
var gamesByRegion: [Region: [Game]] = [:]
|
||||
@@ -510,6 +528,7 @@ final class ScenarioAPlanner: ScenarioPlanner {
|
||||
from: regionGames,
|
||||
stadiums: stadiums,
|
||||
allowRepeatCities: allowRepeatCities,
|
||||
routePreference: routePreference,
|
||||
stopBuilder: buildStops
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user