diff --git a/backend/placeholder_resolver.py b/backend/placeholder_resolver.py index b81e9e7..59bd972 100644 --- a/backend/placeholder_resolver.py +++ b/backend/placeholder_resolver.py @@ -1350,54 +1350,58 @@ def get_placeholder_catalog(profile_id: str) -> Dict[str, List[Dict[str, str]]]: """ Get grouped placeholder catalog with descriptions and example values. + Uses the Placeholder Registry as single source of truth. + Falls back to hardcoded legacy placeholders for non-registry items. + Args: profile_id: User profile ID Returns: Dict mapping category to list of {key, description, example} """ - # Placeholder definitions with descriptions - placeholders = { + from placeholder_registry import get_registry + + catalog = {} + + # Get all registered placeholders from Registry + registry = get_registry() + all_registered = registry.get_all() + + # Group registry placeholders by category + for key, metadata in all_registered.items(): + category = metadata.category + if category not in catalog: + catalog[category] = [] + + # Try to resolve value + try: + if metadata._resolver_func: + example = metadata._resolver_func(profile_id) + else: + # Fallback to PLACEHOLDER_MAP + placeholder = f'{{{{{key}}}}}' + resolver = PLACEHOLDER_MAP.get(placeholder) + if resolver: + example = resolver(profile_id) + else: + example = '[Nicht implementiert]' + except Exception as e: + example = '[Nicht verfügbar]' + + catalog[category].append({ + 'key': key, + 'description': metadata.description, + 'example': str(example) + }) + + # Legacy placeholders (not in registry yet) + legacy_placeholders = { 'Profil': [ ('name', 'Name des Nutzers'), ('age', 'Alter in Jahren'), ('height', 'Körpergröße in cm'), ('geschlecht', 'Geschlecht'), ], - 'Körper': [ - ('weight_aktuell', 'Aktuelles Gewicht in kg'), - ('weight_trend', 'Gewichtstrend (7d/30d)'), - ('kf_aktuell', 'Aktueller Körperfettanteil in %'), - ('bmi', 'Body Mass Index'), - ('weight_7d_median', 'Gewicht 7d Median (kg)'), - ('weight_28d_slope', 'Gewichtstrend 28d (kg/Tag)'), - ('fm_28d_change', 'Fettmasse Änderung 28d (kg)'), - ('lbm_28d_change', 'Magermasse Änderung 28d (kg)'), - ('waist_28d_delta', 'Taillenumfang Änderung 28d (cm)'), - ('waist_hip_ratio', 'Taille/Hüfte-Verhältnis'), - ('recomposition_quadrant', 'Rekomposition-Status'), - ], - 'Ernährung': [ - ('kcal_avg', 'Durchschn. Kalorien (30d)'), - ('protein_avg', 'Durchschn. Protein in g (30d)'), - ('carb_avg', 'Durchschn. Kohlenhydrate in g (30d)'), - ('fat_avg', 'Durchschn. Fett in g (30d)'), - ('energy_balance_7d', 'Energiebilanz 7d (kcal/Tag)'), - ('protein_g_per_kg', 'Protein g/kg Körpergewicht'), - ('protein_adequacy_28d', 'Protein Adequacy Score (0-100)'), - ('macro_consistency_score', 'Makro-Konsistenz Score (0-100)'), - ], - 'Training': [ - ('activity_summary', 'Aktivitäts-Zusammenfassung (7d)'), - ('trainingstyp_verteilung', 'Verteilung nach Trainingstypen'), - ('training_minutes_week', 'Trainingsminuten pro Woche'), - ('training_frequency_7d', 'Trainingshäufigkeit 7d'), - ('quality_sessions_pct', 'Qualitätssessions (%)'), - ('ability_balance_strength', 'Ability Balance - Kraft (0-100)'), - ('ability_balance_endurance', 'Ability Balance - Ausdauer (0-100)'), - ('proxy_internal_load_7d', 'Proxy Load 7d'), - ('rest_day_compliance', 'Ruhetags-Compliance (%)'), - ], 'Schlaf & Erholung': [ ('sleep_avg_duration', 'Durchschn. Schlafdauer (7d)'), ('sleep_avg_quality', 'Durchschn. Schlafqualität (7d)'), @@ -1440,13 +1444,17 @@ def get_placeholder_catalog(profile_id: str) -> Dict[str, List[Dict[str, str]]]: ], } - catalog = {} + # Add legacy placeholders (skip if already in registry) + for category, items in legacy_placeholders.items(): + if category not in catalog: + catalog[category] = [] - for category, items in placeholders.items(): - catalog[category] = [] for key, description in items: + # Skip if already added from registry + if any(p['key'] == key for p in catalog[category]): + continue + placeholder = f'{{{{{key}}}}}' - # Get example value if resolver exists resolver = PLACEHOLDER_MAP.get(placeholder) if resolver: try: