Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
__pycache__/
*.pyc
*.pyo
*.egg-info/
dist/
build/
.env
65 changes: 63 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,63 @@
# Complete-Python-3-Bootcamp
Course Files for Complete Python 3 Bootcamp Course on Udemy
# Ironforge — Evidence-Based Workout Program Designer

A Python CLI tool that generates personalized training programs based on peer-reviewed exercise science (18 papers, 2023-2025).

## How It Works

1. **Intake** — Answers structured questions about goals, training history, schedule, equipment, and individual factors
2. **Algorithm** — Applies evidence-based rules for volume, frequency, exercise selection, load/RIR, and periodization
3. **Output** — Generates a complete mesocycle with per-session exercises, sets, reps, RIR, rest periods, progression, and deload protocols

## Quick Start

```bash
python -m ironforge
```

Or with JSON output:

```bash
python -m ironforge --format json
```

## What It Generates

1. **Training Level Assessment** — Per movement pattern (squat, bench, row, etc.)
2. **Volume Targets** — Weekly fractional sets per muscle group (MEV → MRV)
3. **Split Structure** — Full Body / Upper-Lower / PPL based on available days
4. **Weekly Program** — Each session with exercises, sets × reps, RIR, rest, coaching notes
5. **Progression Instructions** — Linear / Double Progression / Autoregulated based on level
6. **Deload Protocol** — Active deload with reactive triggers
7. **Mesocycle Overview** — 5-week block structure (4 training + 1 deload)

## Key Algorithm Features

- **Fractional set accounting** — Compound exercises give partial credit to secondary muscles
- **Per-pattern classification** — You can be intermediate on bench but beginner on squat
- **97 approved exercises** — Only evidence-backed movements with proper muscle targeting
- **A/B session variation** — Different exercises between repeated session types
- **Antagonist supersets** — Optional time-saving pairing (reduces session time ~36%)
- **Sex-adjusted rep ranges** — Females biased toward higher rep ranges (more Type I fiber)
- **Equipment filtering** — Works for full gyms, limited gyms, and home setups

## Project Structure

```
ironforge/
├── data/ # Exercise database, muscle groups, algorithm constants
├── intake/ # Questionnaire flow and user profile
├── engine/ # Classifier, volume, frequency, selection, load, supersets, periodization
├── program/ # Data models and builder (orchestrator)
└── output/ # Terminal formatter
```

## Requirements

- Python 3.11+
- Zero external dependencies

## Tests

```bash
python -m unittest tests.test_program -v
```
11 changes: 11 additions & 0 deletions api/index.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
"""Vercel serverless entry point — wraps the Flask app."""

import sys
import os

# Add project root to path so ironforge package is importable
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

from ironforge.web import app

# Vercel expects a WSGI-compatible `app` object
Empty file added ironforge/__init__.py
Empty file.
4 changes: 4 additions & 0 deletions ironforge/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from ironforge.cli import main

if __name__ == "__main__":
main()
44 changes: 44 additions & 0 deletions ironforge/cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
"""CLI entry point for Ironforge."""

import argparse
import json
from dataclasses import asdict

from ironforge.intake.flow import run_intake
from ironforge.program.builder import build_program
from ironforge.output.formatter import render


def main():
parser = argparse.ArgumentParser(
description="Ironforge — Evidence-Based Workout Program Designer",
)
parser.add_argument(
"--format", choices=["text", "json"], default="text",
help="Output format (default: text)",
)
args = parser.parse_args()

# Run intake questionnaire
profile = run_intake()

# Build program
program = build_program(profile)

# Output
if args.format == "json":
# Convert enums to strings for JSON serialization
def _convert(obj):
if hasattr(obj, "name") and hasattr(obj, "value"):
return obj.name
raise TypeError(f"Cannot serialize {type(obj)}")

data = asdict(program)
print(json.dumps(data, indent=2, default=_convert))
else:
output = render(program)
print(output)


if __name__ == "__main__":
main()
Empty file added ironforge/data/__init__.py
Empty file.
128 changes: 128 additions & 0 deletions ironforge/data/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
"""Algorithm constants: volume landmarks, rep ranges, RIR targets, rest periods."""

from ironforge.data.muscle_groups import VolumeMuscle, Tier, TrainingLevel
from dataclasses import dataclass


@dataclass(frozen=True)
class VolumeLandmarks:
mev_low: float
mev_high: float
mav_low: float
mav_high: float
mrv: float


# Volume landmarks in fractional sets per week
VOLUME_LANDMARKS: dict[VolumeMuscle, VolumeLandmarks] = {
VolumeMuscle.QUADS: VolumeLandmarks(4, 6, 6, 14, 18),
VolumeMuscle.CHEST: VolumeLandmarks(10, 10, 12, 20, 22),
VolumeMuscle.BACK: VolumeLandmarks(8, 8, 10, 20, 25),
VolumeMuscle.BICEPS: VolumeLandmarks(8, 8, 14, 20, 20),
VolumeMuscle.SIDE_REAR_DELTS: VolumeLandmarks(8, 8, 16, 22, 26),
VolumeMuscle.HAMSTRINGS: VolumeLandmarks(4, 6, 6, 12, 16),
VolumeMuscle.TRICEPS: VolumeLandmarks(4, 4, 8, 18, 18),
VolumeMuscle.CALVES: VolumeLandmarks(12, 12, 12, 18, 20),
VolumeMuscle.GLUTES: VolumeLandmarks(4, 6, 6, 14, 18),
VolumeMuscle.ABS: VolumeLandmarks(2, 4, 4, 10, 14),
VolumeMuscle.FOREARMS: VolumeLandmarks(2, 2, 4, 8, 10),
}


# Rep ranges by tier — all capped to 4-10
REP_RANGES: dict[Tier, tuple[int, int]] = {
Tier.T1: (4, 6),
Tier.T2: (6, 8),
Tier.T3: (8, 10),
}

# Female bias: slightly higher within the 4-10 window
REP_RANGES_FEMALE: dict[Tier, tuple[int, int]] = {
Tier.T1: (5, 8),
Tier.T2: (6, 10),
Tier.T3: (8, 10),
}


# RIR targets by week of mesocycle (1-indexed)
RIR_BY_WEEK: dict[int, int] = {
1: 3,
2: 2,
3: 2,
4: 1,
}

RIR_DELOAD: int = 5

# Beginner RIR override (their calibration is off by 3-5 reps)
RIR_BEGINNER: int = 3

# Rest periods in seconds (min, max)
REST_PERIODS: dict[Tier, tuple[int, int]] = {
Tier.T1: (120, 180),
Tier.T2: (120, 150),
Tier.T3: (90, 120),
}

# Per-session ceiling: direct hard sets per muscle
PER_SESSION_DIRECT_CEILING = 8
PER_SESSION_FRACTIONAL_CEILING = 11

# Maximum sets per exercise per session
MAX_SETS_PER_EXERCISE = 3

# Compound spillover contributions (fractional sets)
# Maps from exercise muscle group categories to secondary contributions
COMPOUND_SPILLOVER = {
"bench_press": {
"primary": "chest",
"secondary": {"triceps": 0.5, "front_delt": 0.5},
},
"overhead_press": {
"primary": "front_delt",
"secondary": {"triceps": 0.5, "side_delt": 0.3},
},
"row": {
"primary": "back",
"secondary": {"biceps": 0.5, "rear_delt": 0.3},
},
"pulldown": {
"primary": "lats",
"secondary": {"biceps": 0.5},
},
"squat": {
"primary": "quads",
"secondary": {"glutes": 0.5},
},
"rdl": {
"primary": "hamstrings",
"secondary": {"glutes": 0.5, "spinal_erectors": 0.5},
},
"hip_thrust": {
"primary": "glutes",
"secondary": {},
},
"dip": {
"primary": "chest",
"secondary": {"triceps": 0.5},
},
}


# Split templates by days per week
SPLIT_TEMPLATES = {
3: "full_body",
4: "upper_lower",
5: "ul_ppl_hybrid",
6: "ppl",
}

# Deload frequency by training level (weeks between deloads)
DELOAD_FREQUENCY: dict[TrainingLevel, tuple[int, int]] = {
TrainingLevel.BEGINNER: (8, 12),
TrainingLevel.INTERMEDIATE: (4, 6),
TrainingLevel.ADVANCED: (3, 5),
}

# Mesocycle length (training weeks only — no forced deload)
MESO_LENGTH = 4
Loading