incorrect location
This commit is contained in:
@@ -1,356 +0,0 @@
|
|||||||
// AITrainingPlanGenerator.swift
|
|
||||||
// AI-powered personalized training plan generation for Nessa
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import Combine
|
|
||||||
|
|
||||||
/// AI Training Plan Generator
|
|
||||||
/// Generates personalized workout plans based on user profile, fitness level,
|
|
||||||
/// workout history, and goals
|
|
||||||
@MainActor
|
|
||||||
class AITrainingPlanGenerator {
|
|
||||||
|
|
||||||
// MARK: - Properties
|
|
||||||
|
|
||||||
private let userService: UserServiceProtocol
|
|
||||||
private let workoutService: WorkoutServiceProtocol
|
|
||||||
private let analyticsService: AnalyticsServiceProtocol
|
|
||||||
private let rateLimiter = RateLimiter(maxRequests: 3, interval: 300) // 3 requests per 5 minutes
|
|
||||||
|
|
||||||
private var cancellables = Set<AnyCancellable>()
|
|
||||||
|
|
||||||
// MARK: - Initialization
|
|
||||||
|
|
||||||
init(userService: UserServiceProtocol = UserService.shared,
|
|
||||||
workoutService: WorkoutServiceProtocol = WorkoutService.shared,
|
|
||||||
analyticsService: AnalyticsServiceProtocol = AnalyticsService.shared) {
|
|
||||||
self.userService = userService
|
|
||||||
self.workoutService = workoutService
|
|
||||||
self.analyticsService = analyticsService
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Public Methods
|
|
||||||
|
|
||||||
/// Generate a personalized training plan for the user
|
|
||||||
/// - Parameter goals: User's fitness goals (strength, endurance, weight loss, etc.)
|
|
||||||
/// - Parameter duration: Preferred weekly training duration in hours
|
|
||||||
/// - Parameter difficulty: Preferred difficulty level
|
|
||||||
/// - Returns: Generated training plan
|
|
||||||
func generatePlan(goals: [GoalType] = [],
|
|
||||||
duration: Double = 5.0,
|
|
||||||
difficulty: DifficultyLevel = .moderate) async throws -> TrainingPlan {
|
|
||||||
|
|
||||||
guard rateLimiter.isAllowed() else {
|
|
||||||
throw RateLimitError.rateLimited
|
|
||||||
}
|
|
||||||
|
|
||||||
let userProfile = await analyzeUserProfile()
|
|
||||||
let progressData = await analyzeProgress()
|
|
||||||
let recommendations = await generateRecommendations(
|
|
||||||
profile: userProfile,
|
|
||||||
goals: goals,
|
|
||||||
currentLevel: userProfile.fitnessLevel,
|
|
||||||
progress: progressData
|
|
||||||
)
|
|
||||||
|
|
||||||
return TrainingPlan(
|
|
||||||
id: UUID().uuidString,
|
|
||||||
userProfile: userProfile,
|
|
||||||
recommendations: recommendations,
|
|
||||||
duration: duration,
|
|
||||||
difficulty: difficulty,
|
|
||||||
generatedAt: Date(),
|
|
||||||
version: 1
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Analyze user profile and determine fitness level
|
|
||||||
func analyzeUserProfile() async throws -> UserProfile {
|
|
||||||
guard let user = await userService.currentUser else {
|
|
||||||
throw ProfileAnalysisError.noUserProfile
|
|
||||||
}
|
|
||||||
|
|
||||||
let workoutHistory = await workoutService.getUserWorkoutHistory(limit: 50)
|
|
||||||
let performanceMetrics = await analyticsService.calculatePerformanceMetrics()
|
|
||||||
|
|
||||||
// Determine fitness level based on workout frequency, intensity, and consistency
|
|
||||||
let workoutFrequency = Double(workoutHistory.count) / 30.0 // workouts per month
|
|
||||||
let avgIntensity = workoutHistory.map { $0.intensity }.average ?? 0.5
|
|
||||||
let consistencyScore = calculateConsistency(workoutHistory)
|
|
||||||
|
|
||||||
let fitnessLevel: FitnessLevel
|
|
||||||
if workoutFrequency >= 4 && avgIntensity >= 0.7 && consistencyScore >= 0.8 {
|
|
||||||
fitnessLevel = .advanced
|
|
||||||
} else if workoutFrequency >= 2 && avgIntensity >= 0.5 && consistencyScore >= 0.6 {
|
|
||||||
fitnessLevel = .intermediate
|
|
||||||
} else if workoutFrequency >= 1 && avgIntensity >= 0.3 && consistencyScore >= 0.4 {
|
|
||||||
fitnessLevel = .beginner
|
|
||||||
} else {
|
|
||||||
fitnessLevel = .absoluteBeginner
|
|
||||||
}
|
|
||||||
|
|
||||||
return UserProfile(
|
|
||||||
userId: user.id,
|
|
||||||
name: user.name,
|
|
||||||
fitnessLevel: fitnessLevel,
|
|
||||||
workoutFrequency: workoutFrequency,
|
|
||||||
avgIntensity: avgIntensity,
|
|
||||||
consistencyScore: consistencyScore,
|
|
||||||
preferences: user.preferences ?? [],
|
|
||||||
injuries: user.injuries ?? []
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Analyze user's progress and trends
|
|
||||||
func analyzeProgress() async throws -> ProgressAnalysis {
|
|
||||||
let recentWorkouts = await workoutService.getRecentWorkouts(limit: 20)
|
|
||||||
let performanceTrends = await analyticsService.calculateTrends(recentWorkouts: recentWorkouts)
|
|
||||||
|
|
||||||
return ProgressAnalysis(
|
|
||||||
totalWorkouts: recentWorkouts.count,
|
|
||||||
avgDuration: recentWorkouts.map { $0.duration }.average ?? 0,
|
|
||||||
avgCalories: recentWorkouts.map { $0.caloriesBurned }.average ?? 0,
|
|
||||||
improvementRate: performanceTrends.improvementRate,
|
|
||||||
consistencyStreak: performanceTrends.streak,
|
|
||||||
plateauDetected: performanceTrends.plateauDetected,
|
|
||||||
injuryRisk: performanceTrends.injuryRisk
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generate personalized recommendations
|
|
||||||
func generateRecommendations(profile: UserProfile,
|
|
||||||
goals: [GoalType],
|
|
||||||
currentLevel: FitnessLevel,
|
|
||||||
progress: ProgressAnalysis) async throws -> [Recommendation] {
|
|
||||||
|
|
||||||
var recommendations: [Recommendation] = []
|
|
||||||
|
|
||||||
// Goal-based recommendations
|
|
||||||
for goal in goals {
|
|
||||||
switch goal {
|
|
||||||
case .strength:
|
|
||||||
recommendations.append(Recommendation(
|
|
||||||
type: .workout,
|
|
||||||
title: "Strength Training Focus",
|
|
||||||
description: "Incorporate 2-3 strength sessions per week with progressive overload",
|
|
||||||
priority: .high,
|
|
||||||
estimatedDuration: 60.0,
|
|
||||||
frequency: 2
|
|
||||||
))
|
|
||||||
case .endurance:
|
|
||||||
recommendations.append(Recommendation(
|
|
||||||
type: .workout,
|
|
||||||
title: "Endurance Building,
|
|
||||||
description: "Longer duration cardio with progressive distance increases",
|
|
||||||
priority: .high,
|
|
||||||
estimatedDuration: 90.0,
|
|
||||||
frequency: 3
|
|
||||||
))
|
|
||||||
case .weightLoss:
|
|
||||||
recommendations.append(Recommendation(
|
|
||||||
type: .workout,
|
|
||||||
title: "HIIT and Cardio Mix",
|
|
||||||
description: "High-intensity intervals mixed with moderate cardio",
|
|
||||||
priority: .high,
|
|
||||||
estimatedDuration: 45.0,
|
|
||||||
frequency: 4
|
|
||||||
))
|
|
||||||
case .flexibility:
|
|
||||||
recommendations.append(Recommendation(
|
|
||||||
type: .workout,
|
|
||||||
title: "Flexibility and Mobility",
|
|
||||||
description: "Daily stretching and mobility sessions",
|
|
||||||
priority: .medium,
|
|
||||||
estimatedDuration: 20.0,
|
|
||||||
frequency: 1
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Level-appropriate recommendations
|
|
||||||
switch currentLevel {
|
|
||||||
case .absoluteBeginner:
|
|
||||||
recommendations.append(Recommendation(
|
|
||||||
type: .tutorial,
|
|
||||||
title: "Beginner Foundation Course",
|
|
||||||
description: "Complete the 4-week foundation program to build basic fitness",
|
|
||||||
priority: .high,
|
|
||||||
estimatedDuration: 30.0,
|
|
||||||
frequency: 3
|
|
||||||
))
|
|
||||||
|
|
||||||
case .beginner:
|
|
||||||
recommendations.append(Recommendation(
|
|
||||||
type: .workout,
|
|
||||||
title: "Progressive Challenge Program",
|
|
||||||
description: "Gradually increase intensity each week",
|
|
||||||
priority: .medium,
|
|
||||||
estimatedDuration: 45.0,
|
|
||||||
frequency: 3
|
|
||||||
))
|
|
||||||
|
|
||||||
case .intermediate:
|
|
||||||
recommendations.append(Recommendation(
|
|
||||||
type: .workout,
|
|
||||||
title: "Advanced Challenge Series",
|
|
||||||
description: "Introduce varied workout types and increased intensity",
|
|
||||||
priority: .medium,
|
|
||||||
estimatedDuration: 60.0,
|
|
||||||
frequency: 3
|
|
||||||
))
|
|
||||||
|
|
||||||
case .advanced:
|
|
||||||
recommendations.append(Recommendation(
|
|
||||||
type: .workout,
|
|
||||||
title: "Elite Performance Program",
|
|
||||||
description: "Specialized training based on specific goals",
|
|
||||||
priority: .high,
|
|
||||||
estimatedDuration: 90.0,
|
|
||||||
frequency: 4
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Progress-based adjustments
|
|
||||||
if progress.plateauDetected {
|
|
||||||
recommendations.append(Recommendation(
|
|
||||||
type: .adjustment,
|
|
||||||
title: "Break Through Plateau",
|
|
||||||
description: "Change workout variety and intensity to break through plateau",
|
|
||||||
priority: .high,
|
|
||||||
estimatedDuration: 0.0,
|
|
||||||
frequency: 0
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
if progress.injuryRisk > 0.7 {
|
|
||||||
recommendations.append(Recommendation(
|
|
||||||
type: .caution,
|
|
||||||
title: "Injury Prevention Focus",
|
|
||||||
description: "Reduce intensity and focus on form; consult a professional",
|
|
||||||
priority: .critical,
|
|
||||||
estimatedDuration: 0.0,
|
|
||||||
frequency: 0
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filter by user preferences and injuries
|
|
||||||
recommendations = recommendations.filter { rec in
|
|
||||||
!rec.title.contains("Injury Prevention") ||
|
|
||||||
(profile.injuries?.contains($0.title.lowercased()) ?? false)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sort by priority
|
|
||||||
return recommendations.sorted { $0.priority > $1.priority }
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Helper Methods
|
|
||||||
|
|
||||||
private func calculateConsistency(_ workouts: [Workout]) -> Double {
|
|
||||||
guard workouts.count > 0 else { return 0 }
|
|
||||||
|
|
||||||
let workoutDates = workouts.map { $0.date }
|
|
||||||
let uniqueWeeks = Set(workoutDates.map { Calendar.current.component(.weekOfYear, from: $0) })
|
|
||||||
let weeksActive = Calendar.current.dateComponents([.calendar], from: workoutDates.first!, to: workoutDates.last!).weekOfYear ?? 0
|
|
||||||
|
|
||||||
guard weeksActive > 0 else { return 0 }
|
|
||||||
|
|
||||||
return Double(uniqueWeeks.count) / Double(weeksActive)
|
|
||||||
}
|
|
||||||
|
|
||||||
private extension Double {
|
|
||||||
var average: Double {
|
|
||||||
guard count > 0 else { return 0 }
|
|
||||||
return sum / count
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Private Types
|
|
||||||
|
|
||||||
private struct UserProfile {
|
|
||||||
let userId: String
|
|
||||||
let name: String
|
|
||||||
let fitnessLevel: FitnessLevel
|
|
||||||
let workoutFrequency: Double
|
|
||||||
let avgIntensity: Double
|
|
||||||
let consistencyScore: Double
|
|
||||||
let preferences: [String]
|
|
||||||
let injuries: [String]
|
|
||||||
}
|
|
||||||
|
|
||||||
private struct ProgressAnalysis {
|
|
||||||
let totalWorkouts: Int
|
|
||||||
let avgDuration: Double
|
|
||||||
let avgCalories: Double
|
|
||||||
let improvementRate: Double
|
|
||||||
let consistencyStreak: Int
|
|
||||||
let plateauDetected: Bool
|
|
||||||
let injuryRisk: Double
|
|
||||||
}
|
|
||||||
|
|
||||||
private struct Recommendation {
|
|
||||||
let type: RecommendationType
|
|
||||||
let title: String
|
|
||||||
let description: String
|
|
||||||
let priority: Priority
|
|
||||||
let estimatedDuration: Double
|
|
||||||
let frequency: Int
|
|
||||||
}
|
|
||||||
|
|
||||||
private struct TrainingPlan {
|
|
||||||
let id: String
|
|
||||||
let userProfile: UserProfile
|
|
||||||
let recommendations: [Recommendation]
|
|
||||||
let duration: Double
|
|
||||||
let difficulty: DifficultyLevel
|
|
||||||
let generatedAt: Date
|
|
||||||
let version: Int
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Enums
|
|
||||||
|
|
||||||
private enum GoalType {
|
|
||||||
case strength
|
|
||||||
case endurance
|
|
||||||
case weightLoss
|
|
||||||
case flexibility
|
|
||||||
}
|
|
||||||
|
|
||||||
private enum DifficultyLevel {
|
|
||||||
case easy
|
|
||||||
case moderate
|
|
||||||
case hard
|
|
||||||
case elite
|
|
||||||
}
|
|
||||||
|
|
||||||
private enum FitnessLevel {
|
|
||||||
case absoluteBeginner
|
|
||||||
case beginner
|
|
||||||
case intermediate
|
|
||||||
case advanced
|
|
||||||
}
|
|
||||||
|
|
||||||
private enum Priority {
|
|
||||||
case critical >
|
|
||||||
case high >
|
|
||||||
case medium >
|
|
||||||
case low
|
|
||||||
}
|
|
||||||
|
|
||||||
private enum RecommendationType {
|
|
||||||
case workout
|
|
||||||
case tutorial
|
|
||||||
case adjustment
|
|
||||||
case caution
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Errors
|
|
||||||
|
|
||||||
private enum RateLimitError: Error {
|
|
||||||
case rateLimited
|
|
||||||
}
|
|
||||||
|
|
||||||
private enum ProfileAnalysisError: Error {
|
|
||||||
case noUserProfile
|
|
||||||
case insufficientData
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user