from typing import Optional from generator.services.exercise_selector import extract_movement_families def focus_key_for_exercise(exercise) -> Optional[str]: """Classify exercise into a coarse focus key used for variety checks.""" if exercise is None: return None families = sorted(extract_movement_families(getattr(exercise, 'name', '') or '')) if families: return families[0] patterns = (getattr(exercise, 'movement_patterns', '') or '').lower() for token in ('upper pull', 'upper push', 'hip hinge', 'squat', 'lunge', 'core', 'carry'): if token in patterns: return token return None def has_duplicate_focus(exercises: list) -> bool: """True when two exercises in one superset map to the same focus key.""" seen = set() for ex in exercises or []: key = focus_key_for_exercise(ex) if not key: continue if key in seen: return True seen.add(key) return False def focus_keys_for_exercises(exercises: list) -> set: """Return non-empty focus keys for a list of exercises.""" keys = set() for ex in exercises or []: key = focus_key_for_exercise(ex) if key: keys.add(key) return keys