""" 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