- Add rules_engine.py with quantitative rules for all 8 workout types - Add quality gate retry loop in generate_single_workout() - Expand calibrate_structure_rules to all 120 combinations (8 types × 5 goals × 3 sections) - Wire WeeklySplitPattern DB records into _pick_weekly_split() - Enforce movement patterns from WorkoutStructureRule in exercise selection - Add straight-set strength support (single main lift, 4-6 rounds) - Add modality consistency check for duration-dominant workout types - Add InjuryStep component to onboarding and preferences - Add sibling exercise exclusion in regenerate and preview_day endpoints - Display generator warnings on dashboard - Expand fix_rep_durations, fix_exercise_flags, fix_movement_pattern_typo - Add audit_exercise_data and check_rules_drift management commands - Add Next.js frontend with dashboard, onboarding, preferences, history pages - Add generator app with ML-powered workout generation pipeline - 96 new tests across 7 test modules Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1376 lines
46 KiB
Python
1376 lines
46 KiB
Python
"""
|
|
Calibrate WorkoutStructureRule DB records for ALL 8 workout types.
|
|
|
|
Creates the full 120-rule matrix (8 types x 5 goals x 3 sections).
|
|
Values are based on exercise science research (workout_research.md),
|
|
not ML extraction. Uses update_or_create for full idempotency.
|
|
|
|
Workout types: traditional_strength_training, hypertrophy,
|
|
high_intensity_interval_training, functional_strength_training,
|
|
cross_training, core_training, flexibility, cardio
|
|
|
|
Goals: strength, hypertrophy, endurance, weight_loss, general_fitness
|
|
Sections: warm_up, working, cool_down
|
|
"""
|
|
from django.core.management.base import BaseCommand
|
|
from generator.models import WorkoutType, WorkoutStructureRule
|
|
|
|
|
|
# ======================================================================
|
|
# Research-backed structure rules — all 8 workout types
|
|
# ======================================================================
|
|
|
|
# traditional_strength_training: heavy compound lifts, low reps, long rest
|
|
# Reference: NSCA Essentials of Strength Training (4th ed.), Schoenfeld 2021
|
|
TRADITIONAL_STRENGTH_RULES = {
|
|
'warm_up': {
|
|
'strength': {
|
|
'rounds': 1, 'ex_per_ss': 5,
|
|
'rep_min': 6, 'rep_max': 8,
|
|
'dur_min': 25, 'dur_max': 35,
|
|
'patterns': [
|
|
'mobility', 'mobility - dynamic', 'core',
|
|
'core - anti-extension', 'lower push',
|
|
],
|
|
},
|
|
'hypertrophy': {
|
|
'rounds': 1, 'ex_per_ss': 5,
|
|
'rep_min': 8, 'rep_max': 10,
|
|
'dur_min': 25, 'dur_max': 30,
|
|
'patterns': [
|
|
'mobility', 'mobility - dynamic', 'core',
|
|
'core - anti-extension', 'lower push',
|
|
],
|
|
},
|
|
'endurance': {
|
|
'rounds': 1, 'ex_per_ss': 6,
|
|
'rep_min': 10, 'rep_max': 12,
|
|
'dur_min': 30, 'dur_max': 40,
|
|
'patterns': [
|
|
'mobility', 'mobility - dynamic', 'core',
|
|
'core - anti-extension', 'cardio/locomotion',
|
|
'lower push',
|
|
],
|
|
},
|
|
'weight_loss': {
|
|
'rounds': 1, 'ex_per_ss': 5,
|
|
'rep_min': 10, 'rep_max': 12,
|
|
'dur_min': 30, 'dur_max': 35,
|
|
'patterns': [
|
|
'mobility', 'mobility - dynamic', 'core',
|
|
'core - anti-extension', 'lower push',
|
|
],
|
|
},
|
|
'general_fitness': {
|
|
'rounds': 1, 'ex_per_ss': 5,
|
|
'rep_min': 8, 'rep_max': 10,
|
|
'dur_min': 25, 'dur_max': 30,
|
|
'patterns': [
|
|
'mobility', 'mobility - dynamic', 'core',
|
|
'core - anti-extension', 'lower push',
|
|
],
|
|
},
|
|
},
|
|
'working': {
|
|
'strength': {
|
|
'rounds': 5, 'ex_per_ss': 2,
|
|
'rep_min': 6, 'rep_max': 8,
|
|
'dur_min': 20, 'dur_max': 30,
|
|
'patterns': [
|
|
'lower push - squat', 'lower pull - hip hinge',
|
|
'upper push - horizontal', 'upper push - vertical',
|
|
'upper pull - horizontal', 'upper pull - vertical',
|
|
'arms',
|
|
],
|
|
},
|
|
'hypertrophy': {
|
|
'rounds': 4, 'ex_per_ss': 2,
|
|
'rep_min': 6, 'rep_max': 10,
|
|
'dur_min': 20, 'dur_max': 30,
|
|
'patterns': [
|
|
'lower push - squat', 'lower pull - hip hinge',
|
|
'upper push - horizontal', 'upper pull - horizontal',
|
|
'upper push - vertical', 'upper pull - vertical',
|
|
'arms',
|
|
],
|
|
},
|
|
'endurance': {
|
|
'rounds': 3, 'ex_per_ss': 3,
|
|
'rep_min': 10, 'rep_max': 15,
|
|
'dur_min': 20, 'dur_max': 30,
|
|
'patterns': [
|
|
'lower push - squat', 'lower pull - hip hinge',
|
|
'upper push', 'upper pull', 'arms', 'core',
|
|
],
|
|
},
|
|
'weight_loss': {
|
|
'rounds': 3, 'ex_per_ss': 3,
|
|
'rep_min': 8, 'rep_max': 12,
|
|
'dur_min': 20, 'dur_max': 30,
|
|
'patterns': [
|
|
'lower push - squat', 'lower pull - hip hinge',
|
|
'upper push', 'upper pull', 'arms', 'core',
|
|
],
|
|
},
|
|
'general_fitness': {
|
|
'rounds': 4, 'ex_per_ss': 2,
|
|
'rep_min': 6, 'rep_max': 10,
|
|
'dur_min': 20, 'dur_max': 30,
|
|
'patterns': [
|
|
'lower push - squat', 'lower pull - hip hinge',
|
|
'upper push - horizontal', 'upper pull - horizontal',
|
|
'upper push - vertical', 'upper pull - vertical',
|
|
'arms',
|
|
],
|
|
},
|
|
},
|
|
'cool_down': {
|
|
'strength': {
|
|
'rounds': 1, 'ex_per_ss': 4,
|
|
'rep_min': 6, 'rep_max': 8,
|
|
'dur_min': 30, 'dur_max': 45,
|
|
'patterns': [
|
|
'mobility', 'mobility - static', 'yoga',
|
|
'lower pull - hip hinge',
|
|
],
|
|
},
|
|
'hypertrophy': {
|
|
'rounds': 1, 'ex_per_ss': 4,
|
|
'rep_min': 8, 'rep_max': 10,
|
|
'dur_min': 25, 'dur_max': 35,
|
|
'patterns': [
|
|
'mobility', 'mobility - static', 'yoga',
|
|
'lower pull - hip hinge',
|
|
],
|
|
},
|
|
'endurance': {
|
|
'rounds': 1, 'ex_per_ss': 4,
|
|
'rep_min': 10, 'rep_max': 12,
|
|
'dur_min': 30, 'dur_max': 45,
|
|
'patterns': [
|
|
'mobility', 'mobility - static', 'yoga',
|
|
'lower pull - hip hinge',
|
|
],
|
|
},
|
|
'weight_loss': {
|
|
'rounds': 1, 'ex_per_ss': 4,
|
|
'rep_min': 10, 'rep_max': 12,
|
|
'dur_min': 25, 'dur_max': 40,
|
|
'patterns': [
|
|
'mobility', 'mobility - static', 'yoga',
|
|
'lower pull - hip hinge',
|
|
],
|
|
},
|
|
'general_fitness': {
|
|
'rounds': 1, 'ex_per_ss': 4,
|
|
'rep_min': 8, 'rep_max': 10,
|
|
'dur_min': 25, 'dur_max': 35,
|
|
'patterns': [
|
|
'mobility', 'mobility - static', 'yoga',
|
|
'lower pull - hip hinge',
|
|
],
|
|
},
|
|
},
|
|
}
|
|
|
|
# hypertrophy: moderate-heavy loads, controlled tempo, volume-focused
|
|
# Reference: Schoenfeld "Science and Development of Muscle Hypertrophy" (2nd ed.)
|
|
HYPERTROPHY_RULES = {
|
|
'warm_up': {
|
|
'strength': {
|
|
'rounds': 1, 'ex_per_ss': 5,
|
|
'rep_min': 6, 'rep_max': 8,
|
|
'dur_min': 25, 'dur_max': 30,
|
|
'patterns': [
|
|
'mobility', 'mobility - dynamic', 'core',
|
|
'core - anti-extension', 'lower push',
|
|
],
|
|
},
|
|
'hypertrophy': {
|
|
'rounds': 1, 'ex_per_ss': 5,
|
|
'rep_min': 8, 'rep_max': 10,
|
|
'dur_min': 25, 'dur_max': 30,
|
|
'patterns': [
|
|
'mobility', 'mobility - dynamic', 'core',
|
|
'core - anti-extension', 'lower push',
|
|
],
|
|
},
|
|
'endurance': {
|
|
'rounds': 1, 'ex_per_ss': 6,
|
|
'rep_min': 10, 'rep_max': 12,
|
|
'dur_min': 30, 'dur_max': 40,
|
|
'patterns': [
|
|
'mobility', 'mobility - dynamic', 'core',
|
|
'cardio/locomotion', 'lower push',
|
|
'core - anti-extension',
|
|
],
|
|
},
|
|
'weight_loss': {
|
|
'rounds': 1, 'ex_per_ss': 5,
|
|
'rep_min': 10, 'rep_max': 12,
|
|
'dur_min': 30, 'dur_max': 35,
|
|
'patterns': [
|
|
'mobility', 'mobility - dynamic', 'core',
|
|
'core - anti-extension', 'lower push',
|
|
],
|
|
},
|
|
'general_fitness': {
|
|
'rounds': 1, 'ex_per_ss': 5,
|
|
'rep_min': 8, 'rep_max': 10,
|
|
'dur_min': 25, 'dur_max': 30,
|
|
'patterns': [
|
|
'mobility', 'mobility - dynamic', 'core',
|
|
'core - anti-extension', 'lower push',
|
|
],
|
|
},
|
|
},
|
|
'working': {
|
|
'strength': {
|
|
'rounds': 4, 'ex_per_ss': 2,
|
|
'rep_min': 6, 'rep_max': 8,
|
|
'dur_min': 20, 'dur_max': 30,
|
|
'patterns': [
|
|
'upper push - horizontal', 'upper push - vertical',
|
|
'upper pull - horizontal', 'upper pull - vertical',
|
|
'lower push - squat', 'lower pull - hip hinge',
|
|
'arms',
|
|
],
|
|
},
|
|
'hypertrophy': {
|
|
'rounds': 3, 'ex_per_ss': 3,
|
|
'rep_min': 8, 'rep_max': 12,
|
|
'dur_min': 20, 'dur_max': 30,
|
|
'patterns': [
|
|
'upper push', 'upper pull', 'lower push',
|
|
'lower pull', 'arms', 'core',
|
|
'lower push - squat', 'lower pull - hip hinge',
|
|
],
|
|
},
|
|
'endurance': {
|
|
'rounds': 3, 'ex_per_ss': 3,
|
|
'rep_min': 12, 'rep_max': 15,
|
|
'dur_min': 25, 'dur_max': 40,
|
|
'patterns': [
|
|
'upper push', 'upper pull', 'lower push',
|
|
'lower pull', 'arms', 'core', 'cardio/locomotion',
|
|
],
|
|
},
|
|
'weight_loss': {
|
|
'rounds': 3, 'ex_per_ss': 3,
|
|
'rep_min': 10, 'rep_max': 15,
|
|
'dur_min': 20, 'dur_max': 35,
|
|
'patterns': [
|
|
'upper push', 'upper pull', 'lower push',
|
|
'lower pull', 'arms', 'core',
|
|
],
|
|
},
|
|
'general_fitness': {
|
|
'rounds': 3, 'ex_per_ss': 3,
|
|
'rep_min': 8, 'rep_max': 12,
|
|
'dur_min': 20, 'dur_max': 30,
|
|
'patterns': [
|
|
'upper push', 'upper pull', 'lower push',
|
|
'lower pull', 'arms', 'core',
|
|
'lower push - squat', 'lower pull - hip hinge',
|
|
],
|
|
},
|
|
},
|
|
'cool_down': {
|
|
'strength': {
|
|
'rounds': 1, 'ex_per_ss': 4,
|
|
'rep_min': 6, 'rep_max': 8,
|
|
'dur_min': 25, 'dur_max': 35,
|
|
'patterns': [
|
|
'mobility', 'mobility - static', 'yoga',
|
|
'lower pull - hip hinge',
|
|
],
|
|
},
|
|
'hypertrophy': {
|
|
'rounds': 1, 'ex_per_ss': 4,
|
|
'rep_min': 8, 'rep_max': 10,
|
|
'dur_min': 25, 'dur_max': 35,
|
|
'patterns': [
|
|
'mobility', 'mobility - static', 'yoga',
|
|
'lower pull - hip hinge',
|
|
],
|
|
},
|
|
'endurance': {
|
|
'rounds': 1, 'ex_per_ss': 5,
|
|
'rep_min': 10, 'rep_max': 12,
|
|
'dur_min': 30, 'dur_max': 45,
|
|
'patterns': [
|
|
'mobility', 'mobility - static', 'yoga',
|
|
'lower pull - hip hinge', 'lower push',
|
|
],
|
|
},
|
|
'weight_loss': {
|
|
'rounds': 1, 'ex_per_ss': 4,
|
|
'rep_min': 10, 'rep_max': 12,
|
|
'dur_min': 25, 'dur_max': 40,
|
|
'patterns': [
|
|
'mobility', 'mobility - static', 'yoga',
|
|
'lower pull - hip hinge',
|
|
],
|
|
},
|
|
'general_fitness': {
|
|
'rounds': 1, 'ex_per_ss': 4,
|
|
'rep_min': 8, 'rep_max': 10,
|
|
'dur_min': 25, 'dur_max': 35,
|
|
'patterns': [
|
|
'mobility', 'mobility - static', 'yoga',
|
|
'lower pull - hip hinge',
|
|
],
|
|
},
|
|
},
|
|
}
|
|
|
|
# high_intensity_interval_training: short work intervals, high tempo circuits
|
|
# Reference: Biddle & Batterham (2015), Tabata protocol research
|
|
# Key: 20-30 min total, 30:30 "Goldilocks ratio", 4-6 exercises per circuit
|
|
HIIT_RULES = {
|
|
'warm_up': {
|
|
'strength': {
|
|
'rounds': 1, 'ex_per_ss': 4,
|
|
'rep_min': 8, 'rep_max': 12,
|
|
'dur_min': 20, 'dur_max': 30,
|
|
'patterns': [
|
|
'mobility - dynamic', 'cardio/locomotion',
|
|
'core', 'lower push',
|
|
],
|
|
},
|
|
'hypertrophy': {
|
|
'rounds': 1, 'ex_per_ss': 4,
|
|
'rep_min': 8, 'rep_max': 12,
|
|
'dur_min': 20, 'dur_max': 30,
|
|
'patterns': [
|
|
'mobility - dynamic', 'cardio/locomotion',
|
|
'core', 'lower push',
|
|
],
|
|
},
|
|
'endurance': {
|
|
'rounds': 1, 'ex_per_ss': 5,
|
|
'rep_min': 10, 'rep_max': 15,
|
|
'dur_min': 20, 'dur_max': 30,
|
|
'patterns': [
|
|
'mobility - dynamic', 'cardio/locomotion',
|
|
'core', 'lower push', 'mobility',
|
|
],
|
|
},
|
|
'weight_loss': {
|
|
'rounds': 1, 'ex_per_ss': 5,
|
|
'rep_min': 10, 'rep_max': 15,
|
|
'dur_min': 20, 'dur_max': 30,
|
|
'patterns': [
|
|
'mobility - dynamic', 'cardio/locomotion',
|
|
'core', 'lower push', 'mobility',
|
|
],
|
|
},
|
|
'general_fitness': {
|
|
'rounds': 1, 'ex_per_ss': 4,
|
|
'rep_min': 8, 'rep_max': 12,
|
|
'dur_min': 20, 'dur_max': 30,
|
|
'patterns': [
|
|
'mobility - dynamic', 'cardio/locomotion',
|
|
'core', 'lower push',
|
|
],
|
|
},
|
|
},
|
|
'working': {
|
|
'strength': {
|
|
'rounds': 4, 'ex_per_ss': 4,
|
|
'rep_min': 8, 'rep_max': 12,
|
|
'dur_min': 20, 'dur_max': 40,
|
|
'patterns': [
|
|
'lower pull - hip hinge', 'upper push',
|
|
'core', 'upper pull', 'lower push - squat',
|
|
'plyometric',
|
|
],
|
|
},
|
|
'hypertrophy': {
|
|
'rounds': 4, 'ex_per_ss': 5,
|
|
'rep_min': 10, 'rep_max': 15,
|
|
'dur_min': 20, 'dur_max': 40,
|
|
'patterns': [
|
|
'upper push', 'upper pull', 'lower push',
|
|
'lower pull', 'core', 'plyometric',
|
|
],
|
|
},
|
|
'endurance': {
|
|
'rounds': 5, 'ex_per_ss': 5,
|
|
'rep_min': 12, 'rep_max': 20,
|
|
'dur_min': 30, 'dur_max': 45,
|
|
'patterns': [
|
|
'cardio/locomotion', 'upper push', 'upper pull',
|
|
'lower push', 'lower pull', 'core', 'plyometric',
|
|
],
|
|
},
|
|
'weight_loss': {
|
|
'rounds': 5, 'ex_per_ss': 5,
|
|
'rep_min': 10, 'rep_max': 20,
|
|
'dur_min': 20, 'dur_max': 40,
|
|
'patterns': [
|
|
'cardio/locomotion', 'upper push', 'upper pull',
|
|
'lower push', 'lower pull', 'core', 'plyometric',
|
|
],
|
|
},
|
|
'general_fitness': {
|
|
'rounds': 4, 'ex_per_ss': 4,
|
|
'rep_min': 10, 'rep_max': 15,
|
|
'dur_min': 20, 'dur_max': 40,
|
|
'patterns': [
|
|
'lower pull - hip hinge', 'upper push',
|
|
'core', 'upper pull', 'lower push - squat',
|
|
'plyometric',
|
|
],
|
|
},
|
|
},
|
|
'cool_down': {
|
|
'strength': {
|
|
'rounds': 1, 'ex_per_ss': 4,
|
|
'rep_min': 8, 'rep_max': 10,
|
|
'dur_min': 30, 'dur_max': 45,
|
|
'patterns': [
|
|
'mobility - static', 'yoga', 'mobility',
|
|
'lower pull - hip hinge',
|
|
],
|
|
},
|
|
'hypertrophy': {
|
|
'rounds': 1, 'ex_per_ss': 4,
|
|
'rep_min': 8, 'rep_max': 10,
|
|
'dur_min': 30, 'dur_max': 45,
|
|
'patterns': [
|
|
'mobility - static', 'yoga', 'mobility',
|
|
'lower pull - hip hinge',
|
|
],
|
|
},
|
|
'endurance': {
|
|
'rounds': 1, 'ex_per_ss': 4,
|
|
'rep_min': 8, 'rep_max': 12,
|
|
'dur_min': 30, 'dur_max': 60,
|
|
'patterns': [
|
|
'mobility - static', 'yoga', 'mobility',
|
|
'cardio/locomotion',
|
|
],
|
|
},
|
|
'weight_loss': {
|
|
'rounds': 1, 'ex_per_ss': 4,
|
|
'rep_min': 8, 'rep_max': 12,
|
|
'dur_min': 30, 'dur_max': 45,
|
|
'patterns': [
|
|
'mobility - static', 'yoga', 'mobility',
|
|
'lower pull - hip hinge',
|
|
],
|
|
},
|
|
'general_fitness': {
|
|
'rounds': 1, 'ex_per_ss': 4,
|
|
'rep_min': 8, 'rep_max': 10,
|
|
'dur_min': 30, 'dur_max': 45,
|
|
'patterns': [
|
|
'mobility - static', 'yoga', 'mobility',
|
|
'lower pull - hip hinge',
|
|
],
|
|
},
|
|
},
|
|
}
|
|
|
|
# functional_strength_training: compound movements, carries, 7 movement patterns
|
|
# Reference: Dan John, Pavel Tsatsouline, NSCA
|
|
# Key: 3-5 sets, 60-180s rest, all 7 patterns represented, carries mandatory
|
|
FUNCTIONAL_STRENGTH_RULES = {
|
|
'warm_up': {
|
|
'strength': {
|
|
'rounds': 1, 'ex_per_ss': 5,
|
|
'rep_min': 6, 'rep_max': 8,
|
|
'dur_min': 25, 'dur_max': 35,
|
|
'patterns': [
|
|
'mobility', 'mobility - dynamic', 'core',
|
|
'core - anti-extension', 'lower push',
|
|
],
|
|
},
|
|
'hypertrophy': {
|
|
'rounds': 1, 'ex_per_ss': 5,
|
|
'rep_min': 8, 'rep_max': 10,
|
|
'dur_min': 25, 'dur_max': 30,
|
|
'patterns': [
|
|
'mobility', 'mobility - dynamic', 'core',
|
|
'core - anti-extension', 'lower push',
|
|
],
|
|
},
|
|
'endurance': {
|
|
'rounds': 1, 'ex_per_ss': 6,
|
|
'rep_min': 10, 'rep_max': 12,
|
|
'dur_min': 30, 'dur_max': 40,
|
|
'patterns': [
|
|
'mobility', 'mobility - dynamic', 'core',
|
|
'core - anti-extension', 'cardio/locomotion',
|
|
'lower push',
|
|
],
|
|
},
|
|
'weight_loss': {
|
|
'rounds': 1, 'ex_per_ss': 5,
|
|
'rep_min': 10, 'rep_max': 12,
|
|
'dur_min': 25, 'dur_max': 35,
|
|
'patterns': [
|
|
'mobility', 'mobility - dynamic', 'core',
|
|
'core - anti-extension', 'lower push',
|
|
],
|
|
},
|
|
'general_fitness': {
|
|
'rounds': 1, 'ex_per_ss': 5,
|
|
'rep_min': 8, 'rep_max': 10,
|
|
'dur_min': 25, 'dur_max': 30,
|
|
'patterns': [
|
|
'mobility', 'mobility - dynamic', 'core',
|
|
'core - anti-extension', 'lower push',
|
|
],
|
|
},
|
|
},
|
|
'working': {
|
|
'strength': {
|
|
'rounds': 4, 'ex_per_ss': 2,
|
|
'rep_min': 6, 'rep_max': 8,
|
|
'dur_min': 20, 'dur_max': 30,
|
|
'patterns': [
|
|
'lower push - squat', 'lower pull - hip hinge',
|
|
'upper push - horizontal', 'upper push - vertical',
|
|
'upper pull - horizontal', 'upper pull - vertical',
|
|
'carry',
|
|
],
|
|
},
|
|
'hypertrophy': {
|
|
'rounds': 4, 'ex_per_ss': 3,
|
|
'rep_min': 8, 'rep_max': 10,
|
|
'dur_min': 20, 'dur_max': 30,
|
|
'patterns': [
|
|
'lower push - squat', 'lower pull - hip hinge',
|
|
'upper push - horizontal', 'upper pull - horizontal',
|
|
'upper push - vertical', 'upper pull - vertical',
|
|
'carry', 'arms',
|
|
],
|
|
},
|
|
'endurance': {
|
|
'rounds': 3, 'ex_per_ss': 3,
|
|
'rep_min': 12, 'rep_max': 20,
|
|
'dur_min': 20, 'dur_max': 30,
|
|
'patterns': [
|
|
'lower push - squat', 'lower pull - hip hinge',
|
|
'upper push', 'upper pull', 'carry',
|
|
'core', 'cardio/locomotion',
|
|
],
|
|
},
|
|
'weight_loss': {
|
|
'rounds': 3, 'ex_per_ss': 3,
|
|
'rep_min': 8, 'rep_max': 12,
|
|
'dur_min': 20, 'dur_max': 30,
|
|
'patterns': [
|
|
'lower push - squat', 'lower pull - hip hinge',
|
|
'upper push', 'upper pull', 'carry',
|
|
'core',
|
|
],
|
|
},
|
|
'general_fitness': {
|
|
'rounds': 4, 'ex_per_ss': 2,
|
|
'rep_min': 6, 'rep_max': 10,
|
|
'dur_min': 20, 'dur_max': 30,
|
|
'patterns': [
|
|
'lower push - squat', 'lower pull - hip hinge',
|
|
'upper push - horizontal', 'upper pull - horizontal',
|
|
'upper push - vertical', 'upper pull - vertical',
|
|
'carry',
|
|
],
|
|
},
|
|
},
|
|
'cool_down': {
|
|
'strength': {
|
|
'rounds': 1, 'ex_per_ss': 4,
|
|
'rep_min': 6, 'rep_max': 8,
|
|
'dur_min': 30, 'dur_max': 45,
|
|
'patterns': [
|
|
'mobility', 'mobility - static', 'yoga',
|
|
'lower pull - hip hinge',
|
|
],
|
|
},
|
|
'hypertrophy': {
|
|
'rounds': 1, 'ex_per_ss': 4,
|
|
'rep_min': 8, 'rep_max': 10,
|
|
'dur_min': 25, 'dur_max': 35,
|
|
'patterns': [
|
|
'mobility', 'mobility - static', 'yoga',
|
|
'lower pull - hip hinge',
|
|
],
|
|
},
|
|
'endurance': {
|
|
'rounds': 1, 'ex_per_ss': 4,
|
|
'rep_min': 10, 'rep_max': 12,
|
|
'dur_min': 30, 'dur_max': 45,
|
|
'patterns': [
|
|
'mobility', 'mobility - static', 'yoga',
|
|
'lower pull - hip hinge',
|
|
],
|
|
},
|
|
'weight_loss': {
|
|
'rounds': 1, 'ex_per_ss': 4,
|
|
'rep_min': 10, 'rep_max': 12,
|
|
'dur_min': 25, 'dur_max': 40,
|
|
'patterns': [
|
|
'mobility', 'mobility - static', 'yoga',
|
|
'lower pull - hip hinge',
|
|
],
|
|
},
|
|
'general_fitness': {
|
|
'rounds': 1, 'ex_per_ss': 4,
|
|
'rep_min': 8, 'rep_max': 10,
|
|
'dur_min': 25, 'dur_max': 35,
|
|
'patterns': [
|
|
'mobility', 'mobility - static', 'yoga',
|
|
'lower pull - hip hinge',
|
|
],
|
|
},
|
|
},
|
|
}
|
|
|
|
# cross_training: mixed modality — strength + WOD formats (AMRAP, EMOM, For Time)
|
|
# Reference: CrossFit methodology, Glassman
|
|
# Key: complexity decreases with fatigue, pull:press 1.5:1, varied formats
|
|
CROSS_TRAINING_RULES = {
|
|
'warm_up': {
|
|
'strength': {
|
|
'rounds': 1, 'ex_per_ss': 5,
|
|
'rep_min': 6, 'rep_max': 10,
|
|
'dur_min': 20, 'dur_max': 30,
|
|
'patterns': [
|
|
'mobility - dynamic', 'core', 'lower push',
|
|
'cardio/locomotion', 'mobility',
|
|
],
|
|
},
|
|
'hypertrophy': {
|
|
'rounds': 1, 'ex_per_ss': 5,
|
|
'rep_min': 8, 'rep_max': 10,
|
|
'dur_min': 20, 'dur_max': 30,
|
|
'patterns': [
|
|
'mobility - dynamic', 'core', 'lower push',
|
|
'cardio/locomotion', 'mobility',
|
|
],
|
|
},
|
|
'endurance': {
|
|
'rounds': 1, 'ex_per_ss': 6,
|
|
'rep_min': 10, 'rep_max': 15,
|
|
'dur_min': 25, 'dur_max': 35,
|
|
'patterns': [
|
|
'mobility - dynamic', 'core', 'lower push',
|
|
'cardio/locomotion', 'mobility',
|
|
'core - anti-extension',
|
|
],
|
|
},
|
|
'weight_loss': {
|
|
'rounds': 1, 'ex_per_ss': 5,
|
|
'rep_min': 10, 'rep_max': 12,
|
|
'dur_min': 20, 'dur_max': 30,
|
|
'patterns': [
|
|
'mobility - dynamic', 'core', 'lower push',
|
|
'cardio/locomotion', 'mobility',
|
|
],
|
|
},
|
|
'general_fitness': {
|
|
'rounds': 1, 'ex_per_ss': 5,
|
|
'rep_min': 8, 'rep_max': 10,
|
|
'dur_min': 20, 'dur_max': 30,
|
|
'patterns': [
|
|
'mobility - dynamic', 'core', 'lower push',
|
|
'cardio/locomotion', 'mobility',
|
|
],
|
|
},
|
|
},
|
|
'working': {
|
|
'strength': {
|
|
'rounds': 4, 'ex_per_ss': 3,
|
|
'rep_min': 6, 'rep_max': 10,
|
|
'dur_min': 20, 'dur_max': 40,
|
|
'patterns': [
|
|
'lower push - squat', 'lower pull - hip hinge',
|
|
'upper push - horizontal', 'upper push - vertical',
|
|
'upper pull - horizontal', 'upper pull - vertical',
|
|
'plyometric',
|
|
],
|
|
},
|
|
'hypertrophy': {
|
|
'rounds': 4, 'ex_per_ss': 3,
|
|
'rep_min': 8, 'rep_max': 12,
|
|
'dur_min': 20, 'dur_max': 40,
|
|
'patterns': [
|
|
'upper push', 'upper pull', 'lower push',
|
|
'lower pull', 'core', 'arms',
|
|
'plyometric',
|
|
],
|
|
},
|
|
'endurance': {
|
|
'rounds': 3, 'ex_per_ss': 4,
|
|
'rep_min': 10, 'rep_max': 15,
|
|
'dur_min': 30, 'dur_max': 45,
|
|
'patterns': [
|
|
'cardio/locomotion', 'upper push', 'upper pull',
|
|
'lower push', 'lower pull', 'core',
|
|
'plyometric',
|
|
],
|
|
},
|
|
'weight_loss': {
|
|
'rounds': 4, 'ex_per_ss': 4,
|
|
'rep_min': 8, 'rep_max': 15,
|
|
'dur_min': 20, 'dur_max': 40,
|
|
'patterns': [
|
|
'cardio/locomotion', 'upper push', 'upper pull',
|
|
'lower push', 'lower pull', 'core',
|
|
'plyometric',
|
|
],
|
|
},
|
|
'general_fitness': {
|
|
'rounds': 4, 'ex_per_ss': 3,
|
|
'rep_min': 6, 'rep_max': 15,
|
|
'dur_min': 20, 'dur_max': 40,
|
|
'patterns': [
|
|
'lower push - squat', 'lower pull - hip hinge',
|
|
'upper push', 'upper pull', 'core',
|
|
'cardio/locomotion', 'plyometric',
|
|
],
|
|
},
|
|
},
|
|
'cool_down': {
|
|
'strength': {
|
|
'rounds': 1, 'ex_per_ss': 4,
|
|
'rep_min': 6, 'rep_max': 10,
|
|
'dur_min': 30, 'dur_max': 45,
|
|
'patterns': [
|
|
'mobility - static', 'yoga', 'mobility',
|
|
'lower pull - hip hinge',
|
|
],
|
|
},
|
|
'hypertrophy': {
|
|
'rounds': 1, 'ex_per_ss': 4,
|
|
'rep_min': 8, 'rep_max': 10,
|
|
'dur_min': 30, 'dur_max': 45,
|
|
'patterns': [
|
|
'mobility - static', 'yoga', 'mobility',
|
|
'lower pull - hip hinge',
|
|
],
|
|
},
|
|
'endurance': {
|
|
'rounds': 1, 'ex_per_ss': 4,
|
|
'rep_min': 8, 'rep_max': 12,
|
|
'dur_min': 30, 'dur_max': 60,
|
|
'patterns': [
|
|
'mobility - static', 'yoga', 'mobility',
|
|
'cardio/locomotion',
|
|
],
|
|
},
|
|
'weight_loss': {
|
|
'rounds': 1, 'ex_per_ss': 4,
|
|
'rep_min': 8, 'rep_max': 12,
|
|
'dur_min': 30, 'dur_max': 45,
|
|
'patterns': [
|
|
'mobility - static', 'yoga', 'mobility',
|
|
'lower pull - hip hinge',
|
|
],
|
|
},
|
|
'general_fitness': {
|
|
'rounds': 1, 'ex_per_ss': 4,
|
|
'rep_min': 8, 'rep_max': 10,
|
|
'dur_min': 30, 'dur_max': 45,
|
|
'patterns': [
|
|
'mobility - static', 'yoga', 'mobility',
|
|
'lower pull - hip hinge',
|
|
],
|
|
},
|
|
},
|
|
}
|
|
|
|
# core_training: anti-movement focus, moderate reps, holds + reps mix
|
|
# Reference: McGill Big 3, anti-movement research
|
|
# Key: anti-extension + anti-rotation + anti-lateral each session, 20-30 min
|
|
CORE_TRAINING_RULES = {
|
|
'warm_up': {
|
|
'strength': {
|
|
'rounds': 1, 'ex_per_ss': 4,
|
|
'rep_min': 8, 'rep_max': 10,
|
|
'dur_min': 20, 'dur_max': 30,
|
|
'patterns': [
|
|
'mobility - dynamic', 'mobility', 'core',
|
|
'cardio/locomotion',
|
|
],
|
|
},
|
|
'hypertrophy': {
|
|
'rounds': 1, 'ex_per_ss': 4,
|
|
'rep_min': 8, 'rep_max': 10,
|
|
'dur_min': 20, 'dur_max': 30,
|
|
'patterns': [
|
|
'mobility - dynamic', 'mobility', 'core',
|
|
'cardio/locomotion',
|
|
],
|
|
},
|
|
'endurance': {
|
|
'rounds': 1, 'ex_per_ss': 4,
|
|
'rep_min': 10, 'rep_max': 12,
|
|
'dur_min': 20, 'dur_max': 30,
|
|
'patterns': [
|
|
'mobility - dynamic', 'mobility', 'core',
|
|
'cardio/locomotion',
|
|
],
|
|
},
|
|
'weight_loss': {
|
|
'rounds': 1, 'ex_per_ss': 4,
|
|
'rep_min': 10, 'rep_max': 12,
|
|
'dur_min': 20, 'dur_max': 30,
|
|
'patterns': [
|
|
'mobility - dynamic', 'mobility', 'core',
|
|
'cardio/locomotion',
|
|
],
|
|
},
|
|
'general_fitness': {
|
|
'rounds': 1, 'ex_per_ss': 4,
|
|
'rep_min': 8, 'rep_max': 10,
|
|
'dur_min': 20, 'dur_max': 30,
|
|
'patterns': [
|
|
'mobility - dynamic', 'mobility', 'core',
|
|
'cardio/locomotion',
|
|
],
|
|
},
|
|
},
|
|
'working': {
|
|
'strength': {
|
|
'rounds': 3, 'ex_per_ss': 3,
|
|
'rep_min': 8, 'rep_max': 12,
|
|
'dur_min': 30, 'dur_max': 60,
|
|
'patterns': [
|
|
'core - anti-extension', 'core - anti-rotation',
|
|
'core - anti-lateral flexion', 'core',
|
|
'carry',
|
|
],
|
|
},
|
|
'hypertrophy': {
|
|
'rounds': 3, 'ex_per_ss': 4,
|
|
'rep_min': 10, 'rep_max': 15,
|
|
'dur_min': 30, 'dur_max': 60,
|
|
'patterns': [
|
|
'core - anti-extension', 'core - anti-rotation',
|
|
'core - anti-lateral flexion', 'core',
|
|
'core - hip flexion', 'carry',
|
|
],
|
|
},
|
|
'endurance': {
|
|
'rounds': 3, 'ex_per_ss': 4,
|
|
'rep_min': 15, 'rep_max': 20,
|
|
'dur_min': 30, 'dur_max': 60,
|
|
'patterns': [
|
|
'core - anti-extension', 'core - anti-rotation',
|
|
'core - anti-lateral flexion', 'core',
|
|
'core - hip flexion', 'carry',
|
|
],
|
|
},
|
|
'weight_loss': {
|
|
'rounds': 3, 'ex_per_ss': 4,
|
|
'rep_min': 12, 'rep_max': 20,
|
|
'dur_min': 30, 'dur_max': 45,
|
|
'patterns': [
|
|
'core - anti-extension', 'core - anti-rotation',
|
|
'core - anti-lateral flexion', 'core',
|
|
'carry', 'cardio/locomotion',
|
|
],
|
|
},
|
|
'general_fitness': {
|
|
'rounds': 3, 'ex_per_ss': 3,
|
|
'rep_min': 10, 'rep_max': 15,
|
|
'dur_min': 30, 'dur_max': 60,
|
|
'patterns': [
|
|
'core - anti-extension', 'core - anti-rotation',
|
|
'core - anti-lateral flexion', 'core',
|
|
'carry',
|
|
],
|
|
},
|
|
},
|
|
'cool_down': {
|
|
'strength': {
|
|
'rounds': 1, 'ex_per_ss': 3,
|
|
'rep_min': 8, 'rep_max': 10,
|
|
'dur_min': 30, 'dur_max': 45,
|
|
'patterns': [
|
|
'mobility - static', 'yoga', 'mobility',
|
|
],
|
|
},
|
|
'hypertrophy': {
|
|
'rounds': 1, 'ex_per_ss': 3,
|
|
'rep_min': 8, 'rep_max': 10,
|
|
'dur_min': 30, 'dur_max': 45,
|
|
'patterns': [
|
|
'mobility - static', 'yoga', 'mobility',
|
|
],
|
|
},
|
|
'endurance': {
|
|
'rounds': 1, 'ex_per_ss': 3,
|
|
'rep_min': 10, 'rep_max': 12,
|
|
'dur_min': 30, 'dur_max': 45,
|
|
'patterns': [
|
|
'mobility - static', 'yoga', 'mobility',
|
|
],
|
|
},
|
|
'weight_loss': {
|
|
'rounds': 1, 'ex_per_ss': 3,
|
|
'rep_min': 10, 'rep_max': 12,
|
|
'dur_min': 30, 'dur_max': 45,
|
|
'patterns': [
|
|
'mobility - static', 'yoga', 'mobility',
|
|
],
|
|
},
|
|
'general_fitness': {
|
|
'rounds': 1, 'ex_per_ss': 3,
|
|
'rep_min': 8, 'rep_max': 10,
|
|
'dur_min': 30, 'dur_max': 45,
|
|
'patterns': [
|
|
'mobility - static', 'yoga', 'mobility',
|
|
],
|
|
},
|
|
},
|
|
}
|
|
|
|
# flexibility: duration-dominant, stretch holds, 1-2 rounds
|
|
# Reference: Cipriani et al. (2012), Bandy & Irion (1994)
|
|
# Key: 45-60s holds, RPE 5-6, hip mobility gets most time, PNF 2-3x/week
|
|
FLEXIBILITY_RULES = {
|
|
'warm_up': {
|
|
'strength': {
|
|
'rounds': 1, 'ex_per_ss': 4,
|
|
'rep_min': 6, 'rep_max': 10,
|
|
'dur_min': 20, 'dur_max': 30,
|
|
'patterns': [
|
|
'mobility - dynamic', 'mobility', 'core',
|
|
'cardio/locomotion',
|
|
],
|
|
},
|
|
'hypertrophy': {
|
|
'rounds': 1, 'ex_per_ss': 4,
|
|
'rep_min': 6, 'rep_max': 10,
|
|
'dur_min': 20, 'dur_max': 30,
|
|
'patterns': [
|
|
'mobility - dynamic', 'mobility', 'core',
|
|
'cardio/locomotion',
|
|
],
|
|
},
|
|
'endurance': {
|
|
'rounds': 1, 'ex_per_ss': 4,
|
|
'rep_min': 8, 'rep_max': 10,
|
|
'dur_min': 20, 'dur_max': 30,
|
|
'patterns': [
|
|
'mobility - dynamic', 'mobility', 'core',
|
|
'cardio/locomotion',
|
|
],
|
|
},
|
|
'weight_loss': {
|
|
'rounds': 1, 'ex_per_ss': 4,
|
|
'rep_min': 8, 'rep_max': 10,
|
|
'dur_min': 20, 'dur_max': 30,
|
|
'patterns': [
|
|
'mobility - dynamic', 'mobility', 'core',
|
|
'cardio/locomotion',
|
|
],
|
|
},
|
|
'general_fitness': {
|
|
'rounds': 1, 'ex_per_ss': 4,
|
|
'rep_min': 6, 'rep_max': 10,
|
|
'dur_min': 20, 'dur_max': 30,
|
|
'patterns': [
|
|
'mobility - dynamic', 'mobility', 'core',
|
|
'cardio/locomotion',
|
|
],
|
|
},
|
|
},
|
|
'working': {
|
|
'strength': {
|
|
'rounds': 2, 'ex_per_ss': 4,
|
|
'rep_min': 6, 'rep_max': 8,
|
|
'dur_min': 45, 'dur_max': 60,
|
|
'patterns': [
|
|
'mobility - static', 'yoga', 'mobility',
|
|
'mobility - dynamic',
|
|
],
|
|
},
|
|
'hypertrophy': {
|
|
'rounds': 2, 'ex_per_ss': 5,
|
|
'rep_min': 6, 'rep_max': 10,
|
|
'dur_min': 45, 'dur_max': 60,
|
|
'patterns': [
|
|
'mobility - static', 'yoga', 'mobility',
|
|
'mobility - dynamic',
|
|
'lower pull - hip hinge',
|
|
],
|
|
},
|
|
'endurance': {
|
|
'rounds': 2, 'ex_per_ss': 5,
|
|
'rep_min': 6, 'rep_max': 10,
|
|
'dur_min': 45, 'dur_max': 60,
|
|
'patterns': [
|
|
'mobility - static', 'yoga', 'mobility',
|
|
'mobility - dynamic',
|
|
'cardio/locomotion',
|
|
],
|
|
},
|
|
'weight_loss': {
|
|
'rounds': 2, 'ex_per_ss': 5,
|
|
'rep_min': 6, 'rep_max': 10,
|
|
'dur_min': 30, 'dur_max': 60,
|
|
'patterns': [
|
|
'mobility - static', 'yoga', 'mobility',
|
|
'mobility - dynamic',
|
|
'cardio/locomotion',
|
|
],
|
|
},
|
|
'general_fitness': {
|
|
'rounds': 2, 'ex_per_ss': 5,
|
|
'rep_min': 6, 'rep_max': 10,
|
|
'dur_min': 45, 'dur_max': 60,
|
|
'patterns': [
|
|
'mobility - static', 'yoga', 'mobility',
|
|
'mobility - dynamic',
|
|
'lower pull - hip hinge',
|
|
],
|
|
},
|
|
},
|
|
'cool_down': {
|
|
'strength': {
|
|
'rounds': 1, 'ex_per_ss': 4,
|
|
'rep_min': 6, 'rep_max': 8,
|
|
'dur_min': 45, 'dur_max': 60,
|
|
'patterns': [
|
|
'mobility - static', 'yoga', 'mobility',
|
|
'lower pull - hip hinge',
|
|
],
|
|
},
|
|
'hypertrophy': {
|
|
'rounds': 1, 'ex_per_ss': 4,
|
|
'rep_min': 6, 'rep_max': 8,
|
|
'dur_min': 45, 'dur_max': 60,
|
|
'patterns': [
|
|
'mobility - static', 'yoga', 'mobility',
|
|
'lower pull - hip hinge',
|
|
],
|
|
},
|
|
'endurance': {
|
|
'rounds': 1, 'ex_per_ss': 4,
|
|
'rep_min': 6, 'rep_max': 10,
|
|
'dur_min': 45, 'dur_max': 60,
|
|
'patterns': [
|
|
'mobility - static', 'yoga', 'mobility',
|
|
'lower pull - hip hinge',
|
|
],
|
|
},
|
|
'weight_loss': {
|
|
'rounds': 1, 'ex_per_ss': 4,
|
|
'rep_min': 6, 'rep_max': 10,
|
|
'dur_min': 30, 'dur_max': 60,
|
|
'patterns': [
|
|
'mobility - static', 'yoga', 'mobility',
|
|
'lower pull - hip hinge',
|
|
],
|
|
},
|
|
'general_fitness': {
|
|
'rounds': 1, 'ex_per_ss': 4,
|
|
'rep_min': 6, 'rep_max': 8,
|
|
'dur_min': 45, 'dur_max': 60,
|
|
'patterns': [
|
|
'mobility - static', 'yoga', 'mobility',
|
|
'lower pull - hip hinge',
|
|
],
|
|
},
|
|
},
|
|
}
|
|
|
|
# cardio: duration-dominant, polarized training (70-80% Zone 2, 20-30% Zone 4-5)
|
|
# Reference: Stöggl & Sperlich (2015), Inigo San Millan Zone 2 research
|
|
# Key: rounds 2-3 (NOT 23-25 from ML), 30-90s duration, steady state
|
|
CARDIO_RULES = {
|
|
'warm_up': {
|
|
'strength': {
|
|
'rounds': 1, 'ex_per_ss': 3,
|
|
'rep_min': 6, 'rep_max': 10,
|
|
'dur_min': 30, 'dur_max': 60,
|
|
'patterns': [
|
|
'mobility - dynamic', 'cardio/locomotion',
|
|
'mobility',
|
|
],
|
|
},
|
|
'hypertrophy': {
|
|
'rounds': 1, 'ex_per_ss': 3,
|
|
'rep_min': 6, 'rep_max': 10,
|
|
'dur_min': 30, 'dur_max': 60,
|
|
'patterns': [
|
|
'mobility - dynamic', 'cardio/locomotion',
|
|
'mobility',
|
|
],
|
|
},
|
|
'endurance': {
|
|
'rounds': 1, 'ex_per_ss': 3,
|
|
'rep_min': 8, 'rep_max': 10,
|
|
'dur_min': 30, 'dur_max': 60,
|
|
'patterns': [
|
|
'mobility - dynamic', 'cardio/locomotion',
|
|
'mobility',
|
|
],
|
|
},
|
|
'weight_loss': {
|
|
'rounds': 1, 'ex_per_ss': 3,
|
|
'rep_min': 8, 'rep_max': 10,
|
|
'dur_min': 30, 'dur_max': 60,
|
|
'patterns': [
|
|
'mobility - dynamic', 'cardio/locomotion',
|
|
'mobility',
|
|
],
|
|
},
|
|
'general_fitness': {
|
|
'rounds': 1, 'ex_per_ss': 3,
|
|
'rep_min': 6, 'rep_max': 10,
|
|
'dur_min': 30, 'dur_max': 60,
|
|
'patterns': [
|
|
'mobility - dynamic', 'cardio/locomotion',
|
|
'mobility',
|
|
],
|
|
},
|
|
},
|
|
'working': {
|
|
'strength': {
|
|
'rounds': 3, 'ex_per_ss': 3,
|
|
'rep_min': 6, 'rep_max': 10,
|
|
'dur_min': 40, 'dur_max': 90,
|
|
'patterns': [
|
|
'cardio/locomotion', 'lower push', 'lower pull',
|
|
],
|
|
},
|
|
'hypertrophy': {
|
|
'rounds': 3, 'ex_per_ss': 3,
|
|
'rep_min': 8, 'rep_max': 12,
|
|
'dur_min': 40, 'dur_max': 90,
|
|
'patterns': [
|
|
'cardio/locomotion', 'lower push', 'lower pull',
|
|
],
|
|
},
|
|
'endurance': {
|
|
'rounds': 3, 'ex_per_ss': 3,
|
|
'rep_min': 8, 'rep_max': 12,
|
|
'dur_min': 45, 'dur_max': 120,
|
|
'patterns': [
|
|
'cardio/locomotion', 'lower push', 'lower pull',
|
|
'core',
|
|
],
|
|
},
|
|
'weight_loss': {
|
|
'rounds': 3, 'ex_per_ss': 3,
|
|
'rep_min': 8, 'rep_max': 12,
|
|
'dur_min': 45, 'dur_max': 90,
|
|
'patterns': [
|
|
'cardio/locomotion', 'lower push', 'lower pull',
|
|
'core',
|
|
],
|
|
},
|
|
'general_fitness': {
|
|
'rounds': 2, 'ex_per_ss': 3,
|
|
'rep_min': 6, 'rep_max': 10,
|
|
'dur_min': 45, 'dur_max': 90,
|
|
'patterns': [
|
|
'cardio/locomotion', 'lower push', 'lower pull',
|
|
],
|
|
},
|
|
},
|
|
'cool_down': {
|
|
'strength': {
|
|
'rounds': 1, 'ex_per_ss': 3,
|
|
'rep_min': 6, 'rep_max': 8,
|
|
'dur_min': 30, 'dur_max': 60,
|
|
'patterns': [
|
|
'mobility - static', 'yoga', 'mobility',
|
|
],
|
|
},
|
|
'hypertrophy': {
|
|
'rounds': 1, 'ex_per_ss': 3,
|
|
'rep_min': 6, 'rep_max': 8,
|
|
'dur_min': 30, 'dur_max': 60,
|
|
'patterns': [
|
|
'mobility - static', 'yoga', 'mobility',
|
|
],
|
|
},
|
|
'endurance': {
|
|
'rounds': 1, 'ex_per_ss': 3,
|
|
'rep_min': 8, 'rep_max': 10,
|
|
'dur_min': 30, 'dur_max': 60,
|
|
'patterns': [
|
|
'mobility - static', 'yoga', 'cardio/locomotion',
|
|
],
|
|
},
|
|
'weight_loss': {
|
|
'rounds': 1, 'ex_per_ss': 3,
|
|
'rep_min': 8, 'rep_max': 10,
|
|
'dur_min': 30, 'dur_max': 60,
|
|
'patterns': [
|
|
'mobility - static', 'yoga', 'mobility',
|
|
],
|
|
},
|
|
'general_fitness': {
|
|
'rounds': 1, 'ex_per_ss': 3,
|
|
'rep_min': 6, 'rep_max': 8,
|
|
'dur_min': 30, 'dur_max': 60,
|
|
'patterns': [
|
|
'mobility - static', 'yoga', 'mobility',
|
|
],
|
|
},
|
|
},
|
|
}
|
|
|
|
# ======================================================================
|
|
# Master mapping: workout_type DB name -> rule dict
|
|
# ======================================================================
|
|
ALL_RULES = {
|
|
'traditional_strength_training': TRADITIONAL_STRENGTH_RULES,
|
|
'hypertrophy': HYPERTROPHY_RULES,
|
|
'high_intensity_interval_training': HIIT_RULES,
|
|
'functional_strength_training': FUNCTIONAL_STRENGTH_RULES,
|
|
'cross_training': CROSS_TRAINING_RULES,
|
|
'core_training': CORE_TRAINING_RULES,
|
|
'flexibility': FLEXIBILITY_RULES,
|
|
'cardio': CARDIO_RULES,
|
|
}
|
|
|
|
# Minimum rep floor — any rule with rep_min below this gets clamped.
|
|
MIN_REPS = 6
|
|
|
|
|
|
class Command(BaseCommand):
|
|
help = (
|
|
'Create/update all 120 WorkoutStructureRule records '
|
|
'(8 types x 5 goals x 3 sections). Fully idempotent.'
|
|
)
|
|
|
|
def add_arguments(self, parser):
|
|
parser.add_argument(
|
|
'--dry-run',
|
|
action='store_true',
|
|
help='Show what would change without applying',
|
|
)
|
|
|
|
def handle(self, *args, **options):
|
|
dry_run = options['dry_run']
|
|
prefix = '[DRY RUN] ' if dry_run else ''
|
|
created_total = 0
|
|
updated_total = 0
|
|
|
|
# ----- Create/update all 8 workout types -----
|
|
for type_name, rules_dict in ALL_RULES.items():
|
|
c, u = self._upsert_rules_for_type(
|
|
type_name, rules_dict, dry_run, prefix,
|
|
)
|
|
created_total += c
|
|
updated_total += u
|
|
|
|
# ----- Fix all sub-floor rep_min values -----
|
|
fixed = self._fix_rep_floors(dry_run, prefix)
|
|
|
|
self.stdout.write(
|
|
f'\n{prefix}Done: created {created_total}, '
|
|
f'updated {updated_total}, fixed {fixed} rep floors'
|
|
)
|
|
total = WorkoutStructureRule.objects.count()
|
|
self.stdout.write(f'{prefix}Total rules in DB: {total}')
|
|
|
|
def _upsert_rules_for_type(self, type_name, rules_dict, dry_run, prefix):
|
|
"""Create or update all WorkoutStructureRules for a workout type."""
|
|
try:
|
|
wt = WorkoutType.objects.get(name=type_name)
|
|
except WorkoutType.DoesNotExist:
|
|
self.stderr.write(
|
|
f' WorkoutType "{type_name}" not found, skipping'
|
|
)
|
|
return 0, 0
|
|
|
|
created_count = 0
|
|
updated_count = 0
|
|
self.stdout.write(f'\n{prefix}Processing {type_name}:')
|
|
|
|
for section_type, goals in rules_dict.items():
|
|
for goal_type, vals in goals.items():
|
|
defaults = {
|
|
'typical_rounds': vals['rounds'],
|
|
'typical_exercises_per_superset': vals['ex_per_ss'],
|
|
'typical_rep_range_min': vals['rep_min'],
|
|
'typical_rep_range_max': vals['rep_max'],
|
|
'typical_duration_range_min': vals['dur_min'],
|
|
'typical_duration_range_max': vals['dur_max'],
|
|
'movement_patterns': vals['patterns'],
|
|
}
|
|
|
|
if dry_run:
|
|
exists = WorkoutStructureRule.objects.filter(
|
|
workout_type=wt,
|
|
section_type=section_type,
|
|
goal_type=goal_type,
|
|
).exists()
|
|
action = 'update' if exists else 'create'
|
|
else:
|
|
_, was_created = WorkoutStructureRule.objects.update_or_create(
|
|
workout_type=wt,
|
|
section_type=section_type,
|
|
goal_type=goal_type,
|
|
defaults=defaults,
|
|
)
|
|
action = 'create' if was_created else 'update'
|
|
|
|
if action == 'create':
|
|
created_count += 1
|
|
else:
|
|
updated_count += 1
|
|
|
|
self.stdout.write(
|
|
f' {action}: {section_type}/{goal_type} '
|
|
f'rounds={vals["rounds"]}, ex/ss={vals["ex_per_ss"]}, '
|
|
f'reps={vals["rep_min"]}-{vals["rep_max"]}, '
|
|
f'dur={vals["dur_min"]}-{vals["dur_max"]}s'
|
|
)
|
|
|
|
return created_count, updated_count
|
|
|
|
def _fix_rep_floors(self, dry_run, prefix):
|
|
"""Clamp all rep_min values below MIN_REPS to MIN_REPS."""
|
|
fixed = 0
|
|
rules = WorkoutStructureRule.objects.filter(
|
|
typical_rep_range_min__lt=MIN_REPS,
|
|
typical_rep_range_min__gt=0,
|
|
)
|
|
|
|
if rules.exists():
|
|
self.stdout.write(
|
|
f'\n{prefix}Fixing sub-floor rep_min values '
|
|
f'(minimum {MIN_REPS}):'
|
|
)
|
|
|
|
for rule in rules:
|
|
old_min = rule.typical_rep_range_min
|
|
old_max = rule.typical_rep_range_max
|
|
new_min = MIN_REPS
|
|
new_max = max(old_max, new_min)
|
|
|
|
changes = [f'rep_min: {old_min} -> {new_min}']
|
|
if new_max != old_max:
|
|
changes.append(f'rep_max: {old_max} -> {new_max}')
|
|
|
|
wt_name = rule.workout_type.name if rule.workout_type else 'Any'
|
|
self.stdout.write(
|
|
f' {wt_name}/{rule.section_type}/{rule.goal_type}: '
|
|
f'{", ".join(changes)}'
|
|
)
|
|
|
|
if not dry_run:
|
|
rule.typical_rep_range_min = new_min
|
|
rule.typical_rep_range_max = new_max
|
|
rule.save()
|
|
fixed += 1
|
|
|
|
if not rules.exists():
|
|
self.stdout.write(
|
|
f'\n{prefix}No sub-floor rep_min values found'
|
|
)
|
|
|
|
return fixed
|