Skip to main content

A Python library for creating, parsing, and converting structured workout formats with support for parametrization.

Project description

Structured Workouts

A Python library for creating, parsing, and converting structured workout formats. Built around the Structured Workout Format (SWF), this package enables you to work with workouts from various platforms and convert between different file formats.

Quick Start

Installation

pip install structured-workouts

Or using uv:

uv add structured-workouts

Basic Usage

Creating a Workout

from structured_workouts import Workout
from structured_workouts.base import (
    Interval, Section, ConstantValue, Value, Reference,
    VolumeQuantity, IntensityQuantity
)

# Create a simple 30-minute threshold workout
workout = Workout(
    title="Threshold Test",
    description="30-minute sustained effort at threshold power",
    content=[
        Section(
            name="Warmup",
            content=[
                Interval(
                    volume=ConstantValue(
                        value=Value(reference=Reference.absolute, value=10*60),  # 10 minutes (in seconds)
                        quantity=VolumeQuantity.duration
                    ),
                    intensity=ConstantValue(
                        value=Value(reference=Reference.parameter, value=0.6, parameter="threshold"),
                        quantity=IntensityQuantity.power
                    )
                )
            ]
        ),
        Section(
            name="Main Set", 
            content=[
                Interval(
                    volume=ConstantValue(
                        value=Value(reference=Reference.absolute, value=30*60),  # 30 minutes
                        quantity=VolumeQuantity.duration
                    ),
                    intensity=ConstantValue(
                        value=Value(reference=Reference.parameter, value=1.0, parameter="threshold"),
                        quantity=IntensityQuantity.power
                    )
                )
            ]
        ),
        Section(
            name="Cooldown",
            content=[
                Interval(
                    volume=ConstantValue(
                        value=Value(reference=Reference.absolute, value=10*60),  # 10 minutes
                        quantity=VolumeQuantity.duration
                    ),
                    intensity=ConstantValue(
                        value=Value(reference=Reference.parameter, value=0.5, parameter="threshold"),
                        quantity=IntensityQuantity.power
                    )
                )
            ]
        )
    ]
)

print(f"Created workout: {workout.title}")

Converting Between Formats

from structured_workouts.parsers import IntervalsICUTextParser

# Export to intervals.icu text format
parser = IntervalsICUTextParser()
intervals_icu_text = parser.to_format(workout)
print("Intervals.icu format:")
print(intervals_icu_text)

Output:

Warmup
- 10m 60%

Main Set
- 30m 100%

Cooldown
- 10m 50%

Using the Command Line Interface

Convert workouts between formats using the CLI:

# Convert from SWF to intervals.icu text format
structured-workouts --from swf --from-file my_workout.json --to intervals-icu-text

# List all available formats
structured-workouts --list-formats

# Convert with automatic format detection
structured-workouts --from-file workout.json

Structured Workout Format (SWF)

The Structured Workout Format (SWF) is a JSON-based format designed to be:

  • Simple and readable: Easy to understand and modify
  • Flexible: Supports complex workout structures with repeats, sections, and variable intensities
  • Convertible: Can serve as an intermediate format for converting between other workout formats
  • Parametrizable: Supports variables for power zones, heart rate zones, etc.

Key Concepts

  • Duration: Always specified in seconds (e.g., 600 for 10 minutes)
  • Intensity: Power or speed, specified using three reference types:
    • absolute: Direct values (e.g., 250 watts, 4.5 m/s)
    • parameter: Fractions of named variables (e.g., 85% of "threshold")
    • tte: Time-to-exhaustion based (e.g., "power you can hold for 300 seconds")
  • Variables: Named references like "threshold", "vo2max" for flexible intensity targeting

For detailed format specification, see README_SWF.md.

Supported Formats

✅ Currently Supported

Format Import Export Notes
SWF (JSON) Native format
Intervals.icu Text ⚠️ Import is experimental and not feature complete
Intervals.icu API JSON ⚠️ Import is experimental and not feature complete

🚧 Planned Support

  • ERG files
  • MRC files
  • ZWO files (Zwift)
  • Garmin Connect JSON format
  • FIT files

Advanced Usage

Working with Variables

# Create a workout using power zones
workout = Workout(
    title="Power Zone Training",
    content=[
        Interval(
            volume=ConstantValue(
                value=Value(reference=Reference.absolute, value=20*60),
                quantity=VolumeQuantity.duration
            ),
            intensity=ConstantValue(
                value=Value(
                    reference=Reference.parameter, 
                    value=0.85,  # 85% of FTP
                    parameter="ftp"  # Functional Threshold Power
                ),
                quantity=IntensityQuantity.power
            )
        )
    ]
)

Complex Workouts with Repeats

from structured_workouts.base import Repeat, RepeatQuantity

# Create intervals: 5x (4 minutes on, 2 minutes recovery)
intervals = Repeat(
    type="repeat",
    count=ConstantValue(
        value=Value(reference=Reference.absolute, value=5),
        quantity=RepeatQuantity.NUMBER
    ),
    content=[
        Interval(
            volume=ConstantValue(
                value=Value(reference=Reference.absolute, value=4*60),
                quantity=VolumeQuantity.duration
            ),
            intensity=ConstantValue(
                value=Value(reference=Reference.parameter, value=1.05, parameter="threshold"),
                quantity=IntensityQuantity.power
            )
        ),
        Interval(
            volume=ConstantValue(
                value=Value(reference=Reference.absolute, value=2*60),
                quantity=VolumeQuantity.duration
            ),
            intensity=ConstantValue(
                value=Value(reference=Reference.parameter, value=0.6, parameter="threshold"),
                quantity=IntensityQuantity.power
            )
        )
    ]
)

workout = Workout(
    title="VO2 Max Intervals",
    content=[Section(name="Intervals", content=[intervals])]
)

Time-to-Exhaustion (TTE) Based Intensities

The new schema supports specifying intensities based on time-to-exhaustion:

# Create a workout using TTE-based intensities
workout = Workout(
    title="TTE Workout",
    content=[
        Interval(
            volume=ConstantValue(
                value=Value(reference=Reference.absolute, value=8*60),  # 8 minutes
                quantity=VolumeQuantity.duration
            ),
            intensity=ConstantValue(
                value=Value(reference=Reference.tte, value=300),  # Power you can hold for 300s (5 min)
                quantity=IntensityQuantity.power
            )
        )
    ]
)

Development

Running Tests

make test

Project Structure

src/structured_workouts/
├── __init__.py           # Main package exports
├── base.py              # Core data structures (Interval, Section, etc.)
├── schema.py            # Pydantic models and validation
├── structured_workout_format.py  # SWF format implementation
├── validation.py        # Additional validation logic
└── parsers/
    ├── base.py          # Abstract parser base class
    ├── intervals_icu_text.py     # Intervals.icu text format parser
    └── intervals_icu_api.py      # Intervals.icu API JSON parser

Upload to Intervals.icu

The package includes a complete example script that creates a workout and uploads it to intervals.icu:

# Set up your API credentials
export INTERVALS_ICU_API_KEY="your_api_key"
export INTERVALS_ICU_ATHLETE_ID="i"

# Run the upload script
uv run scripts/upload_to_intervals_icu.py

See scripts/upload_to_intervals_icu.py for a complete working example.

Contributing

Contributions are welcome! Areas where help is especially appreciated:

  1. New format parsers (ERG, MRC, ZWO, etc.)
  2. Improving import functionality for intervals.icu formats
  3. Test coverage for edge cases
  4. Documentation and examples

License

This project is licensed under the MIT License - see the LICENSE file for details.

Background

Although structured-workouts is developed by and integrated with SweatStack, it is designed to be a general-purpose library not tied to any specific platform or service.

The main goal is to provide a flexible, standardized way to represent structured workouts that can serve as an intermediate format for converting between different platforms and training systems.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

structured_workouts-0.2.1.tar.gz (53.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

structured_workouts-0.2.1-py3-none-any.whl (19.2 kB view details)

Uploaded Python 3

File details

Details for the file structured_workouts-0.2.1.tar.gz.

File metadata

  • Download URL: structured_workouts-0.2.1.tar.gz
  • Upload date:
  • Size: 53.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.12.5

File hashes

Hashes for structured_workouts-0.2.1.tar.gz
Algorithm Hash digest
SHA256 cfb67d9c0d558cbb15ead2f8e5ad3f5b23dd0eb1827a0ac1da0b3ea5e78c44e4
MD5 8f84d650b65834fced954a93d32f079f
BLAKE2b-256 31b12a6e95be1f2d698c14f20f23dc9dbdbb2427242710e70c970799fdd5da6f

See more details on using hashes here.

File details

Details for the file structured_workouts-0.2.1-py3-none-any.whl.

File metadata

File hashes

Hashes for structured_workouts-0.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 990e18eca67bc46e95db1d3e3f730463b1284f86e7993f45aa0f6a2e8476c80d
MD5 9ea6b31dc88f85d9cea4fef53613e408
BLAKE2b-256 f8422bd01773eec67f157d067aaab8253a3ed904e39ffa59e5772c3759a7b3bc

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page