Skip to main content

Time Series

Overview

The application implements a time series system for tracking runner performance metrics over time. This architecture enables historical data preservation, period-appropriate calculations, and automatic performance metric updates.

Data Structure

All time series data follows a consistent structure (see Database Schema):

{
"timestamp": ISODate("2025-01-15T00:00:00.000Z"),
"value": { /* metric-specific data */ },
"source": "session_upload", // How the value was obtained
"notes": "Optional description"
}

Applied to:

  • Heart Rate Zones (heart_rate_zones_history) - Training zone evolution
  • Personal Bests (pb_history) - Best times across multiple distances
  • Performance Metrics (performance_metrics_time_series) - Fitness marker progression

Sequential Processing Workflow

1. File Upload & Sorting

User uploads FIT files (single or ZIP)

Extract all FIT files from ZIP archives

Extract session dates from each FIT file

Sort files chronologically (oldest first)

Process sequentially

Implementation: app/file_processor.py:process_uploaded_files()

2. Session Processing with Time Series

For each session (oldest to newest):

1. Get session date from FIT file

2. Query time series for values at that date
- Heart rate zones active at session date
- Personal bests current at session date
- Critical speed valid at session date

3. Calculate statistics using period-appropriate values

4. Detect if any personal bests were achieved

5. If PB detected → Update PB time series

6. If PB updated → Recalculate critical speed

7. Move to next session (repeat)

3. Automatic PB Detection & Updates

Detection Logic:

# For each tracked distance in session
current_pb = get_value_at_date(pb_history, session_date)
session_time = extract_segment_time(session, distance)

if session_time < current_pb:
# New PB detected!
add_value(pb_history,
value={'distance': distance, 'seconds': session_time},
timestamp=session_date,
source='session_upload')

4. Critical Speed Recalculation

When PBs are updated, Critical Speed is automatically recalculated using the updated values at that point in time.

5. Historical Upload Cascade

When a session older than 7 days is uploaded:

Detect historical upload (session_date < now - 7 days)

Check if newer sessions exist

If yes → Trigger cascade recalculation

Reprocess all sessions after the historical date

Ensures statistics use updated PBs/CS

Key Components

ComponentResponsibilityLocation
Time Series UtilsGeneric time series operationsapp/utils/time_series.py
Runner ServiceUpdate HR zones, PBs, CSapp/services/runner_service.py
File ProcessorSequential session processingapp/file_processor.py
Statistics ProcessorPeriod-aware calculationsapp/statistics_processor.py
REST APITime series CRUD operationsapp/routes.py

Time Series Utility Functions

Located in app/utils/time_series.py:

  • get_value_at_date(time_series, date) - Binary search to find value active at specific date
  • get_latest_value(time_series) - Get most recent value
  • add_value(time_series, value, timestamp, source) - Add new entry, maintaining chronological order
  • get_value_history(time_series, start_date, end_date) - Query date range

REST API Endpoints

EndpointMethodPurpose
/api/runners/<id>/heart-rate-zonesGETGet HR zones history
/api/runners/<id>/heart-rate-zonesPOSTUpdate HR zones
/api/runners/<id>/personal-bestsGETGet PB history
/api/runners/<id>/personal-bestsPOSTUpdate PB
/api/runners/<id>/critical-speedGETGet CS history
/api/runners/<id>/critical-speedPOSTUpdate CS
/api/defaults/scheduleGETGet global training schedule defaults

Benefits Achieved

  1. Historical Preservation - No data loss when values change
  2. Period-Appropriate Calculations - Sessions use values valid at their date
  3. Automatic Maintenance - PBs and CS update without manual intervention
  4. Chronological Consistency - Upload order doesn't affect results
  5. Retroactive Corrections - Historical uploads trigger necessary recalculations

Migration

Time series was implemented in three phases:

  1. Migration 003 - Heart rate zones → time series
  2. Migration 004 - Personal bests → time series
  3. Migration 005 - Critical speed → time series

All existing data was preserved during migration. Legacy fields remain in the schema for backward compatibility.

For complete schema details, see Database Schema.