""" Django management command to analyze existing workouts and extract ML patterns. Usage: python manage.py analyze_workouts python manage.py analyze_workouts --dry-run python manage.py analyze_workouts --verbosity 2 """ import time from django.core.management.base import BaseCommand from generator.services.workout_analyzer import WorkoutAnalyzer from generator.models import ( MuscleGroupSplit, MovementPatternOrder, WeeklySplitPattern, WorkoutStructureRule, WorkoutType, ) class Command(BaseCommand): help = ( 'Analyze existing workouts in the database and extract ML patterns ' 'into WorkoutType, MuscleGroupSplit, WeeklySplitPattern, ' 'WorkoutStructureRule, and MovementPatternOrder models.' ) def add_arguments(self, parser): parser.add_argument( '--dry-run', action='store_true', default=False, help='Print what would be done without writing to the database.', ) def handle(self, *args, **options): dry_run = options.get('dry_run', False) if dry_run: self.stdout.write(self.style.WARNING( 'DRY RUN mode - no changes will be written to the database.\n' 'Remove --dry-run to actually run the analysis.\n' )) self._print_current_state() return start_time = time.time() analyzer = WorkoutAnalyzer() analyzer.analyze() elapsed = time.time() - start_time self.stdout.write('') self._print_current_state() self.stdout.write(self.style.SUCCESS( f'\nAnalysis complete in {elapsed:.2f}s!' )) def _print_current_state(self): """Print a summary of the current state of all ML pattern models.""" self.stdout.write(self.style.MIGRATE_HEADING('\nCurrent ML Pattern Model State:')) self.stdout.write(f' WorkoutType: {WorkoutType.objects.count()} records') self.stdout.write(f' MuscleGroupSplit: {MuscleGroupSplit.objects.count()} records') self.stdout.write(f' WeeklySplitPattern: {WeeklySplitPattern.objects.count()} records') self.stdout.write(f' WorkoutStructureRule: {WorkoutStructureRule.objects.count()} records') self.stdout.write(f' MovementPatternOrder: {MovementPatternOrder.objects.count()} records') # List WorkoutTypes wts = WorkoutType.objects.all().order_by('name') if wts.exists(): self.stdout.write(self.style.MIGRATE_HEADING('\n WorkoutTypes:')) for wt in wts: self.stdout.write( f' - {wt.name}: reps {wt.rep_range_min}-{wt.rep_range_max}, ' f'rounds {wt.round_range_min}-{wt.round_range_max}, ' f'intensity={wt.typical_intensity}' ) # List MuscleGroupSplits splits = MuscleGroupSplit.objects.all().order_by('-frequency') if splits.exists(): self.stdout.write(self.style.MIGRATE_HEADING('\n Top MuscleGroupSplits:')) for s in splits[:10]: muscles_str = ', '.join(s.muscle_names[:5]) if len(s.muscle_names) > 5: muscles_str += f' (+{len(s.muscle_names) - 5} more)' self.stdout.write( f' - [{s.split_type}] {s.label} | ' f'freq={s.frequency}, ex_count={s.typical_exercise_count} | ' f'{muscles_str}' ) # List WeeklySplitPatterns patterns = WeeklySplitPattern.objects.all().order_by('-frequency') if patterns.exists(): self.stdout.write(self.style.MIGRATE_HEADING('\n Top WeeklySplitPatterns:')) for p in patterns[:10]: self.stdout.write( f' - {p.days_per_week}-day: {p.pattern_labels} ' f'(freq={p.frequency}, rest_days={p.rest_day_positions})' ) # List WorkoutStructureRule goal distribution rules = WorkoutStructureRule.objects.all() if rules.exists(): from collections import Counter goal_counts = Counter(rules.values_list('goal_type', flat=True)) self.stdout.write(self.style.MIGRATE_HEADING('\n WorkoutStructureRule by goal:')) for goal, count in sorted(goal_counts.items()): self.stdout.write(f' - {goal}: {count} rules')