Spaces:
Running
Running
| import type { Activity } from '@/types'; | |
| // This file is deprecated - functionality moved to metricAcwr.ts | |
| // Kept for reference only | |
| interface ACWRData { | |
| dates: string[]; | |
| acwr: (number | null)[]; | |
| acuteLoad: (number | null)[]; | |
| chronicLoad: (number | null)[]; | |
| } | |
| /** | |
| * Calculate Acute-Chronic Workload Ratio (ACWR) | |
| * Acute load: 7-day rolling average | |
| * Chronic load: 28-day rolling average | |
| * ACWR = Acute / Chronic | |
| * Optimal range: 0.8 - 1.3 | |
| */ | |
| export function calculateACWR(activities: Activity[], dateRange?: { start: Date; end: Date }): ACWRData { | |
| if (activities.length === 0 && !dateRange) { | |
| return { dates: [], acwr: [], acuteLoad: [], chronicLoad: [] }; | |
| } | |
| const dates: string[] = []; | |
| const acwr: (number | null)[] = []; | |
| const acuteLoad: (number | null)[] = []; | |
| const chronicLoad: (number | null)[] = []; | |
| // Create a map of date to total load (using TSS, or distance as fallback) | |
| const dailyLoad = new Map<string, number>(); | |
| activities.forEach(activity => { | |
| const dateKey = activity.date.toISOString().split('T')[0]; | |
| const load = activity.trainingStressScore || activity.distance || 0; | |
| if (!dailyLoad.has(dateKey)) { | |
| dailyLoad.set(dateKey, 0); | |
| } | |
| dailyLoad.set(dateKey, dailyLoad.get(dateKey)! + load); | |
| }); | |
| // Get all dates in range | |
| const startDate = dateRange?.start || (activities.length > 0 ? activities[0].date : new Date()); | |
| const endDate = dateRange?.end || (activities.length > 0 ? activities[activities.length - 1].date : new Date()); | |
| const allDates: Date[] = []; | |
| for (let d = new Date(startDate); d <= endDate; d.setDate(d.getDate() + 1)) { | |
| allDates.push(new Date(d)); | |
| } | |
| // Calculate ACWR for each date | |
| allDates.forEach((date, index) => { | |
| const dateKey = date.toISOString().split('T')[0]; | |
| dates.push(dateKey); | |
| // Need at least 28 days of data for chronic load | |
| if (index < 27) { | |
| acuteLoad.push(null); | |
| chronicLoad.push(null); | |
| acwr.push(null); | |
| return; | |
| } | |
| // Calculate acute load (7-day average) | |
| let acuteSum = 0; | |
| for (let i = 0; i < 7; i++) { | |
| const d = allDates[index - i]; | |
| const key = d.toISOString().split('T')[0]; | |
| acuteSum += dailyLoad.get(key) || 0; | |
| } | |
| const acuteAvg = acuteSum / 7; | |
| // Calculate chronic load (28-day average) | |
| let chronicSum = 0; | |
| for (let i = 0; i < 28; i++) { | |
| const d = allDates[index - i]; | |
| const key = d.toISOString().split('T')[0]; | |
| chronicSum += dailyLoad.get(key) || 0; | |
| } | |
| const chronicAvg = chronicSum / 28; | |
| acuteLoad.push(acuteAvg); | |
| chronicLoad.push(chronicAvg); | |
| // Calculate ACWR | |
| if (chronicAvg > 0) { | |
| acwr.push(acuteAvg / chronicAvg); | |
| } else { | |
| acwr.push(null); | |
| } | |
| }); | |
| return { dates, acwr, acuteLoad, chronicLoad }; | |
| } | |