Unraid deployment fixes and generator improvements
- Add Next.js rewrites to proxy API calls through same origin (fixes login/media on werkout.treytartt.com) - Fix mediaUrl() in DayCard and ExerciseRow to use relative paths in production - Add proxyTimeout for long-running workout generation endpoints - Add CSRF trusted origin for treytartt.com - Split docker-compose into production (Unraid) and dev configs - Show display_name and descriptions on workout type cards - Generator: rules engine improvements, movement enforcement, exercise selector updates - Add new test files for rules drift, workout research generation Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,123 @@
|
||||
from django.core.management import call_command
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
WORKOUT_TYPE_CALIBRATION = {
|
||||
'functional_strength_training': {
|
||||
'typical_rest_between_sets': 60,
|
||||
'typical_intensity': 'medium',
|
||||
'rep_range_min': 8,
|
||||
'rep_range_max': 15,
|
||||
'round_range_min': 3,
|
||||
'round_range_max': 4,
|
||||
'duration_bias': 0.15,
|
||||
'superset_size_min': 2,
|
||||
'superset_size_max': 4,
|
||||
},
|
||||
'traditional_strength_training': {
|
||||
'typical_rest_between_sets': 120,
|
||||
'typical_intensity': 'high',
|
||||
'rep_range_min': 4,
|
||||
'rep_range_max': 8,
|
||||
'round_range_min': 3,
|
||||
'round_range_max': 5,
|
||||
'duration_bias': 0.1,
|
||||
'superset_size_min': 1,
|
||||
'superset_size_max': 3,
|
||||
},
|
||||
'high_intensity_interval_training': {
|
||||
'typical_rest_between_sets': 30,
|
||||
'typical_intensity': 'high',
|
||||
'rep_range_min': 10,
|
||||
'rep_range_max': 20,
|
||||
'round_range_min': 3,
|
||||
'round_range_max': 5,
|
||||
'duration_bias': 0.7,
|
||||
'superset_size_min': 3,
|
||||
'superset_size_max': 6,
|
||||
},
|
||||
'cross_training': {
|
||||
'typical_rest_between_sets': 45,
|
||||
'typical_intensity': 'high',
|
||||
'rep_range_min': 8,
|
||||
'rep_range_max': 15,
|
||||
'round_range_min': 3,
|
||||
'round_range_max': 5,
|
||||
'duration_bias': 0.4,
|
||||
'superset_size_min': 3,
|
||||
'superset_size_max': 5,
|
||||
},
|
||||
'core_training': {
|
||||
'typical_rest_between_sets': 30,
|
||||
'typical_intensity': 'medium',
|
||||
'rep_range_min': 10,
|
||||
'rep_range_max': 20,
|
||||
'round_range_min': 2,
|
||||
'round_range_max': 4,
|
||||
'duration_bias': 0.5,
|
||||
'superset_size_min': 3,
|
||||
'superset_size_max': 5,
|
||||
},
|
||||
'flexibility': {
|
||||
'typical_rest_between_sets': 15,
|
||||
'typical_intensity': 'low',
|
||||
'rep_range_min': 1,
|
||||
'rep_range_max': 5,
|
||||
'round_range_min': 1,
|
||||
'round_range_max': 2,
|
||||
'duration_bias': 0.9,
|
||||
'superset_size_min': 3,
|
||||
'superset_size_max': 6,
|
||||
},
|
||||
'cardio': {
|
||||
'typical_rest_between_sets': 30,
|
||||
'typical_intensity': 'medium',
|
||||
'rep_range_min': 1,
|
||||
'rep_range_max': 1,
|
||||
'round_range_min': 1,
|
||||
'round_range_max': 3,
|
||||
'duration_bias': 1.0,
|
||||
'superset_size_min': 1,
|
||||
'superset_size_max': 3,
|
||||
},
|
||||
'hypertrophy': {
|
||||
'typical_rest_between_sets': 90,
|
||||
'typical_intensity': 'high',
|
||||
'rep_range_min': 8,
|
||||
'rep_range_max': 15,
|
||||
'round_range_min': 3,
|
||||
'round_range_max': 4,
|
||||
'duration_bias': 0.2,
|
||||
'superset_size_min': 2,
|
||||
'superset_size_max': 4,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def apply_calibration(apps, schema_editor):
|
||||
WorkoutType = apps.get_model('generator', 'WorkoutType')
|
||||
|
||||
for type_name, fields in WORKOUT_TYPE_CALIBRATION.items():
|
||||
defaults = dict(fields)
|
||||
defaults.setdefault('display_name', type_name.replace('_', ' ').title())
|
||||
defaults.setdefault('description', '')
|
||||
WorkoutType.objects.update_or_create(name=type_name, defaults=defaults)
|
||||
|
||||
# Ensure the full 8 x 5 x 3 = 120 structure-rule matrix is present and calibrated.
|
||||
call_command('calibrate_structure_rules')
|
||||
|
||||
|
||||
def noop_reverse(apps, schema_editor):
|
||||
# Intentionally no-op: this migration normalizes live calibration data.
|
||||
pass
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('generator', '0005_add_periodization_fields'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(apply_calibration, noop_reverse),
|
||||
]
|
||||
121
generator/migrations/0007_force_recalibration.py
Normal file
121
generator/migrations/0007_force_recalibration.py
Normal file
@@ -0,0 +1,121 @@
|
||||
from django.core.management import call_command
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
WORKOUT_TYPE_CALIBRATION = {
|
||||
'functional_strength_training': {
|
||||
'typical_rest_between_sets': 60,
|
||||
'typical_intensity': 'medium',
|
||||
'rep_range_min': 8,
|
||||
'rep_range_max': 15,
|
||||
'round_range_min': 3,
|
||||
'round_range_max': 4,
|
||||
'duration_bias': 0.15,
|
||||
'superset_size_min': 2,
|
||||
'superset_size_max': 4,
|
||||
},
|
||||
'traditional_strength_training': {
|
||||
'typical_rest_between_sets': 120,
|
||||
'typical_intensity': 'high',
|
||||
'rep_range_min': 4,
|
||||
'rep_range_max': 8,
|
||||
'round_range_min': 3,
|
||||
'round_range_max': 5,
|
||||
'duration_bias': 0.1,
|
||||
'superset_size_min': 1,
|
||||
'superset_size_max': 3,
|
||||
},
|
||||
'high_intensity_interval_training': {
|
||||
'typical_rest_between_sets': 30,
|
||||
'typical_intensity': 'high',
|
||||
'rep_range_min': 10,
|
||||
'rep_range_max': 20,
|
||||
'round_range_min': 3,
|
||||
'round_range_max': 5,
|
||||
'duration_bias': 0.7,
|
||||
'superset_size_min': 3,
|
||||
'superset_size_max': 6,
|
||||
},
|
||||
'cross_training': {
|
||||
'typical_rest_between_sets': 45,
|
||||
'typical_intensity': 'high',
|
||||
'rep_range_min': 8,
|
||||
'rep_range_max': 15,
|
||||
'round_range_min': 3,
|
||||
'round_range_max': 5,
|
||||
'duration_bias': 0.4,
|
||||
'superset_size_min': 3,
|
||||
'superset_size_max': 5,
|
||||
},
|
||||
'core_training': {
|
||||
'typical_rest_between_sets': 30,
|
||||
'typical_intensity': 'medium',
|
||||
'rep_range_min': 10,
|
||||
'rep_range_max': 20,
|
||||
'round_range_min': 2,
|
||||
'round_range_max': 4,
|
||||
'duration_bias': 0.5,
|
||||
'superset_size_min': 3,
|
||||
'superset_size_max': 5,
|
||||
},
|
||||
'flexibility': {
|
||||
'typical_rest_between_sets': 15,
|
||||
'typical_intensity': 'low',
|
||||
'rep_range_min': 1,
|
||||
'rep_range_max': 5,
|
||||
'round_range_min': 1,
|
||||
'round_range_max': 2,
|
||||
'duration_bias': 0.9,
|
||||
'superset_size_min': 3,
|
||||
'superset_size_max': 6,
|
||||
},
|
||||
'cardio': {
|
||||
'typical_rest_between_sets': 30,
|
||||
'typical_intensity': 'medium',
|
||||
'rep_range_min': 1,
|
||||
'rep_range_max': 1,
|
||||
'round_range_min': 1,
|
||||
'round_range_max': 3,
|
||||
'duration_bias': 1.0,
|
||||
'superset_size_min': 1,
|
||||
'superset_size_max': 3,
|
||||
},
|
||||
'hypertrophy': {
|
||||
'typical_rest_between_sets': 90,
|
||||
'typical_intensity': 'high',
|
||||
'rep_range_min': 8,
|
||||
'rep_range_max': 15,
|
||||
'round_range_min': 3,
|
||||
'round_range_max': 4,
|
||||
'duration_bias': 0.2,
|
||||
'superset_size_min': 2,
|
||||
'superset_size_max': 4,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def apply_calibration(apps, schema_editor):
|
||||
WorkoutType = apps.get_model('generator', 'WorkoutType')
|
||||
|
||||
for type_name, fields in WORKOUT_TYPE_CALIBRATION.items():
|
||||
defaults = dict(fields)
|
||||
defaults.setdefault('display_name', type_name.replace('_', ' ').title())
|
||||
defaults.setdefault('description', '')
|
||||
WorkoutType.objects.update_or_create(name=type_name, defaults=defaults)
|
||||
|
||||
call_command('calibrate_structure_rules')
|
||||
|
||||
|
||||
def noop_reverse(apps, schema_editor):
|
||||
pass
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('generator', '0006_calibrate_workout_types_and_structure_rules'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(apply_calibration, noop_reverse),
|
||||
]
|
||||
Reference in New Issue
Block a user