Running Manager is an open-source Streamlit application for managing trail running coaching. It provides coaches and athletes with tools to plan training sessions, track activities from Strava or Garmin, analyze performance metrics, and manage running goals.
Key screenshots from the application to give a quick visual overview:
- Weekly Planner: Create and manage training sessions with flexible session types (fundamental endurance, long runs, intervals, races)
- Session Templates: Save and reuse session configurations for consistent planning
- Strava Integration: OAuth-based sync with automatic activity import (14-day window, incremental updates)
- Activity Linking: Manual matching of planned sessions with logged activities; RPE tracking
- Performance Analytics:
- Planned vs actual workload comparison (weekly/daily)
- Training load metrics (acute/chronic TRIMP, distance-equivalent)
- Multi-metric visualization (time, distance, DistEq, TRIMP)
- Speed-effort scatter plots
- Advanced Metrics:
- Distance-equivalent (DistEq) calculations accounting for elevation gain
- Sport-specific equivalence (e.g., bike ride DistEq factors)
- TRIMP (Training Impulse) using HR reserve weighting
- Rolling training loads (7-day acute, 28-day chronic)
- Interval Workouts: Structured interval step editor with repeatable blocks and recovery phases
- Garmin Support: (Stub) Framework for future Garmin Connect integration
- FR/EN Locale: Fr-FR formatting for European users; extensible to other languages
- Python 3.11 or later
- uv package manager (recommended) or pip
# Clone the repository
git clone https://github.com/piromagnus/RunningManager
cd RunningManager
# Install dependencies
uv sync
rm -rf data/* # Clean the data directory to start fresh
# Or with pip:
# pip install -r requirements.txt
# Run the application
uv run streamlit run app.pyThe app will open at http://localhost:8501.
Create a .env file in the project root:
cp .env.example .envEdit .env with your configuration:
# Data storage directory
DATA_DIR=./data
# Strava API Integration (optional)
STRAVA_CLIENT_ID=your_client_id
STRAVA_CLIENT_SECRET=your_client_secret
STRAVA_REDIRECT_URI=http://localhost:8501/callback
# Encryption key for token storage (required if using Strava)
ENCRYPTION_KEY=<generate-via-command-below>
# Mapbox for premium basemaps (optional)
MAPBOX_API_KEY=your_mapbox_tokenpython3 -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"- Go to Strava API Settings
- Create an app with:
- Application Name: RunningManager
- Category: Other
- Callback Domain:
localhost(dev) or your domain (prod)
- Copy
Client IDandClient Secretto.env - Set
STRAVA_REDIRECT_URI(default:"http://localhost:8501/Settings")
- Create account at Mapbox
- Generate access token
- Add to
.envasMAPBOX_API_KEY
uv run streamlit run app.py- Planner: Create and manage weekly training sessions
- Activities: View activity feed, link unlinked sessions, browse activity details
- Dashboard: Training load trends and performance analytics
- Analytics: Planned vs actual metrics with flexible date ranges and category filters
- Settings: Strava integration, metrics recomputation, thresholds and goals configuration
- Athlete: Athlete profile management
- Goals: Set and track race/performance goals
# Run the full test suite
pytest
# Run with coverage
pytest --cov# Lint code (Ruff, line length 100)
ruff check .
# Format code
ruff format .RunningManager/
├── app.py (Streamlit entry)
├── pages/ (UI multi-page app)
│ ├── Planner.py: Session planning
│ ├── Dashboard.py: Training load trends
│ ├── Analytics.py: Planned vs actual analysis
│ ├── Activities.py: Activity feed & linking
│ └── Settings.py: Integrations & config
├── widgets/ (Reusable UI components)
│ ├── athlete_selector.py: Athlete selection widget
│ ├── session_forms.py: Session type form renderers
│ ├── template_selector.py: Template selection UI
│ ├── comparison_panel.py: Activity comparison display
│ └── ...
├── graph/ (Visualization components)
│ ├── training_load.py: Acute/chronic load charts
│ ├── hr_speed.py: HR vs Speed scatter plots
│ ├── elevation.py: Elevation profile with grade coloring
│ ├── analytics.py: Planned vs actual bar charts
│ └── ...
├── services/ (Domain logic)
│ ├── planner_service.py: Estimation (pace, distance, TRIMP)
│ ├── metrics_service.py: Metrics pipeline
│ ├── analytics_service.py: Analytics computations
│ ├── strava_service.py: OAuth & sync
│ ├── dashboard_data_service.py: Dashboard data loading
│ └── ...
├── persistence/ (CSV storage)
│ ├── csv_storage.py: Pandas + portalocker IO
│ └── repositories.py: CRUD repos
└── utils/ (Helpers)
├── config.py: Env loading & secrets
├── formatting.py: fr-FR locale display
├── segments.py: Segment merging utilities
├── dashboard_state.py: Dashboard state management
├── elevation_preprocessing.py: Elevation data preprocessing
└── ...CSV Tables (stored in DATA_DIR, default ./data/):
activities.csv: Imported from Strava/Garminplanned_sessions.csv: Coach-created training sessionslinks.csv: Manual matches between sessions and activitiesactivities_metrics.csv: Computed per-activity metricsplanned_metrics.csv: Computed per-session estimatesweekly_metrics.csv: Weekly aggregates (planned/actual)daily_metrics.csv: Daily aggregates with rolling windowsathletes.csv,settings.csv,thresholds.csv,goals.csv,templates.csv, etc.
Timeseries: data/timeseries/{activityId}.csv — sampled HR, pace, elevation, etc.
- CSV Decimals: Always use
.(period) as decimal separator; never locale-specific commas - Locale Display: Use
utils/formatting.pyfor UI display only; storage keeps raw decimals - File Locking: All CSV reads/writes use
portalockerfor safe concurrent access - Secrets: Never hardcode; load via
.env; encrypt sensitive tokens with Fernet
Contributions are welcome! Please follow these guidelines:
- Fork the repository
- Create a feature branch:
git checkout -b feature/your-feature-name - Install dev dependencies:
uv sync - Make your changes
- Run tests:
pytest - Lint:
ruff check . && ruff format . - Commit with clear messages:
git commit -m "feat: add feature X" - Push and create a Pull Request
- Line Length: 100 characters (Ruff configured)
- Imports: Sorted with Ruff (isort rules)
- Format: Black-compatible formatting via Ruff
- Type Hints: Use type annotations for public APIs
- Testing: Add tests for new behavior; maintain >80% coverage
- File Organization: Keep files under 500 lines; extract reusable components to
widgets/and visualizations tograph/
- Bug Fixes: Report issues on GitHub; PRs welcome
- Features: Garmin integration, additional analytics, TCX export enhancements
- Documentation: Improve guides, add examples, translate to other languages
- Performance: Optimize CSV loading, chart rendering, metrics computation
- Testing: Expand test coverage, add integration tests
- UI/UX: Improve interface clarity, accessibility, internationalization
When reporting bugs, please include:
- Python version and OS
- Steps to reproduce
- Expected vs actual behavior
- Relevant logs or error messages
- Ensure all tests pass:
pytest - Update
AGENTS.mdordocs/if adding/changing architecture - Add tests for new features or bug fixes
- Keep commits focused and descriptive
- Reference related issues (e.g.,
Fixes #123)
This project is licensed under the GNU General Public License v3.0 (GPLv3). See LICENSE for full details.
Summary: You are free to use, modify, and distribute this software, provided that:
- You retain the copyright notice and license
- You provide a copy of the license with any distribution
- Any modifications are also licensed under GPLv3
- You disclose the source code if distributing the software
- Issues: Report bugs and request features on GitHub Issues
- Discussions: Join conversations on GitHub Discussions
- Documentation: See
AGENTS.mdfor developer guidelines
Thanks to all contributors who have helped improve Running Manager!
Running Manager is built by a trail running enthusiast, for coaches and athletes who want sophisticated training analytics in open source. We welcome your contributions and feedback!



