""" Activity Metrics Placeholder Registrations Registers all 17 activity-related placeholders in the central placeholder registry. Evidence-based metadata with clear tagging of source. Groups: - Legacy Resolver (3): activity_summary, activity_detail, trainingstyp_verteilung - Basic Metrics (7): training_minutes_week, training_frequency_7d, quality_sessions_pct, proxy_internal_load_7d, monotony_score, strain_score, rest_day_compliance - Advanced Metrics (7): ability_balance_*, vo2max_trend_28d, activity_score """ from placeholder_registry import ( PlaceholderMetadata, MissingValuePolicy, EvidenceType, OutputType, PlaceholderType, register_placeholder ) # ============================================================================= # GRUPPE 1: Legacy Resolver (3 Placeholders) # ============================================================================= def register_activity_group_1(): """ Register Group 1: Legacy Resolver placeholders. These use old resolver pattern (direct formatting in resolver, no data layer). """ # ── activity_summary ────────────────────────────────────────────────────── activity_summary_metadata = PlaceholderMetadata( key="activity_summary", category="Aktivität", description="Zusammenfassung der letzten 14 Tage Aktivität", resolver_module="backend/placeholder_resolver.py", resolver_function="_format_activity_summary", data_layer_module=None, data_layer_function=None, source_tables=["activity_log", "training_types"], semantic_contract=( "Liefert eine kompakte textuelle Zusammenfassung der Trainingsaktivitäten " "der letzten 14 Tage. Beinhaltet: Anzahl Einheiten, Gesamtdauer, " "Trainingstypen-Verteilung (Top 3), durchschnittliche Dauer pro Einheit." ), business_meaning=( "Schneller Überblick für KI-Prompts über aktuelle Trainingsaktivität. " "Erlaubt Prompt-Autoren, Trainingsvolumen und -vielfalt auf einen Blick " "zu erfassen ohne einzelne Datenpunkte zu konsumieren." ), unit="text", time_window="14d", output_type=OutputType.TEXT_SUMMARY, placeholder_type=PlaceholderType.INTERPRETED, format_hint="Mehrere Sätze, menschenlesbar, Deutsch", example_output=( "14 Einheiten (315 min gesamt). Top-Typen: Krafttraining (5x, 180 min), " "Ausdauer (4x, 90 min), Mobilität (3x, 30 min). Ø Dauer: 22 min/Einheit." ), minimum_data_requirements=None, quality_filter_policy=None, confidence_logic=( "Keine formale Confidence-Berechnung. Verfügbarkeit = mindestens 1 " "activity_log Eintrag in 14d." ), missing_value_policy=MissingValuePolicy( available=False, value_raw=None, missing_reason="no_data", legacy_display="Keine Aktivitätsdaten" ), known_limitations=( "OLD RESOLVER PATTERN: Keine Data Layer Funktion. Formatierung direkt " "im Resolver. JOIN mit training_types für Typ-Namen. " "CRITICAL: Keine Qualitätsprüfung - auch 1-Minuten-Einheiten werden gezählt." ), layer_1_decision="NONE - Old resolver pattern (direct SQL in resolver)", layer_2a_decision="Placeholder Resolver (formatting + SQL query)", layer_2b_reuse_possible=False, architecture_alignment=( "NOT ALIGNED with Phase 0c Multi-Layer Architecture. " "Should be refactored to use data_layer function." ), issue_53_alignment="NOT ALIGNED - no layer separation" ) activity_summary_metadata.set_evidence("key", EvidenceType.CODE_DERIVED) activity_summary_metadata.set_evidence("category", EvidenceType.CODE_DERIVED) activity_summary_metadata.set_evidence("description", EvidenceType.DRAFT_DERIVED) activity_summary_metadata.set_evidence("resolver_module", EvidenceType.CODE_DERIVED) activity_summary_metadata.set_evidence("resolver_function", EvidenceType.CODE_DERIVED) activity_summary_metadata.set_evidence("data_layer_module", EvidenceType.CODE_DERIVED) activity_summary_metadata.set_evidence("data_layer_function", EvidenceType.CODE_DERIVED) activity_summary_metadata.set_evidence("source_tables", EvidenceType.CODE_DERIVED) activity_summary_metadata.set_evidence("semantic_contract", EvidenceType.MIXED) activity_summary_metadata.set_evidence("business_meaning", EvidenceType.DRAFT_DERIVED) activity_summary_metadata.set_evidence("unit", EvidenceType.CODE_DERIVED) activity_summary_metadata.set_evidence("time_window", EvidenceType.CODE_DERIVED) activity_summary_metadata.set_evidence("output_type", EvidenceType.CODE_DERIVED) activity_summary_metadata.set_evidence("placeholder_type", EvidenceType.MIXED) activity_summary_metadata.set_evidence("format_hint", EvidenceType.CODE_DERIVED) activity_summary_metadata.set_evidence("example_output", EvidenceType.CODE_DERIVED) activity_summary_metadata.set_evidence("minimum_data_requirements", EvidenceType.UNRESOLVED) activity_summary_metadata.set_evidence("quality_filter_policy", EvidenceType.UNRESOLVED) activity_summary_metadata.set_evidence("confidence_logic", EvidenceType.CODE_DERIVED) activity_summary_metadata.set_evidence("missing_value_policy", EvidenceType.CODE_DERIVED) activity_summary_metadata.set_evidence("known_limitations", EvidenceType.CODE_DERIVED) activity_summary_metadata.set_evidence("layer_1_decision", EvidenceType.CODE_DERIVED) activity_summary_metadata.set_evidence("layer_2a_decision", EvidenceType.CODE_DERIVED) activity_summary_metadata.set_evidence("layer_2b_reuse_possible", EvidenceType.CODE_DERIVED) activity_summary_metadata.set_evidence("architecture_alignment", EvidenceType.CODE_DERIVED) activity_summary_metadata.set_evidence("issue_53_alignment", EvidenceType.CODE_DERIVED) register_placeholder(activity_summary_metadata) # ── activity_detail ─────────────────────────────────────────────────────── activity_detail_metadata = PlaceholderMetadata( key="activity_detail", category="Aktivität", description="Detaillierte Liste der letzten 14 Tage Aktivität", resolver_module="backend/placeholder_resolver.py", resolver_function="_format_activity_detail", data_layer_module=None, data_layer_function=None, source_tables=["activity_log", "training_types"], semantic_contract=( "Liefert eine strukturierte Liste aller Trainingseinheiten der letzten 14 Tage. " "Jede Einheit: Datum, Trainingstyp, Dauer (Minuten), optional Notizen. " "Sortiert chronologisch absteigend (neueste zuerst)." ), business_meaning=( "Detaillierte Trainingshistorie für KI-Prompts, die Muster, Progressionen " "oder Abweichungen analysieren sollen. Erlaubt Erkennung von Trainingszyklen, " "Pausentagen, Intensitätsmustern." ), unit="list", time_window="14d", output_type=OutputType.LIST, placeholder_type=PlaceholderType.RAW_DATA, format_hint="Liste von Strings, eine Zeile pro Einheit: 'YYYY-MM-DD: Typ (Dauer min)'", example_output=( "2026-03-28: Krafttraining (45 min)\\n" "2026-03-27: Laufen (30 min)\\n" "2026-03-25: Yoga (20 min)" ), minimum_data_requirements=None, quality_filter_policy=None, confidence_logic="Keine Confidence-Berechnung. Rohdaten-Liste.", missing_value_policy=MissingValuePolicy( available=False, value_raw=None, missing_reason="no_data", legacy_display="Keine Aktivitätsdaten" ), known_limitations=( "OLD RESOLVER PATTERN: Keine Data Layer Funktion. " "Formatierung direkt im Resolver. " "CRITICAL: Keine Qualitätsfilterung - auch ungültige Einheiten (z.B. 0 min) " "werden gelistet. JOIN mit training_types für Typ-Namen." ), layer_1_decision="NONE - Old resolver pattern (direct SQL in resolver)", layer_2a_decision="Placeholder Resolver (formatting + SQL query)", layer_2b_reuse_possible=False, architecture_alignment=( "NOT ALIGNED with Phase 0c Multi-Layer Architecture. " "Should be refactored to use data_layer function." ), issue_53_alignment="NOT ALIGNED - no layer separation" ) activity_detail_metadata.set_evidence("key", EvidenceType.CODE_DERIVED) activity_detail_metadata.set_evidence("category", EvidenceType.CODE_DERIVED) activity_detail_metadata.set_evidence("description", EvidenceType.DRAFT_DERIVED) activity_detail_metadata.set_evidence("resolver_module", EvidenceType.CODE_DERIVED) activity_detail_metadata.set_evidence("resolver_function", EvidenceType.CODE_DERIVED) activity_detail_metadata.set_evidence("data_layer_module", EvidenceType.CODE_DERIVED) activity_detail_metadata.set_evidence("data_layer_function", EvidenceType.CODE_DERIVED) activity_detail_metadata.set_evidence("source_tables", EvidenceType.CODE_DERIVED) activity_detail_metadata.set_evidence("semantic_contract", EvidenceType.MIXED) activity_detail_metadata.set_evidence("business_meaning", EvidenceType.DRAFT_DERIVED) activity_detail_metadata.set_evidence("unit", EvidenceType.CODE_DERIVED) activity_detail_metadata.set_evidence("time_window", EvidenceType.CODE_DERIVED) activity_detail_metadata.set_evidence("output_type", EvidenceType.CODE_DERIVED) activity_detail_metadata.set_evidence("placeholder_type", EvidenceType.MIXED) activity_detail_metadata.set_evidence("format_hint", EvidenceType.CODE_DERIVED) activity_detail_metadata.set_evidence("example_output", EvidenceType.CODE_DERIVED) activity_detail_metadata.set_evidence("minimum_data_requirements", EvidenceType.UNRESOLVED) activity_detail_metadata.set_evidence("quality_filter_policy", EvidenceType.UNRESOLVED) activity_detail_metadata.set_evidence("confidence_logic", EvidenceType.CODE_DERIVED) activity_detail_metadata.set_evidence("missing_value_policy", EvidenceType.CODE_DERIVED) activity_detail_metadata.set_evidence("known_limitations", EvidenceType.CODE_DERIVED) activity_detail_metadata.set_evidence("layer_1_decision", EvidenceType.CODE_DERIVED) activity_detail_metadata.set_evidence("layer_2a_decision", EvidenceType.CODE_DERIVED) activity_detail_metadata.set_evidence("layer_2b_reuse_possible", EvidenceType.CODE_DERIVED) activity_detail_metadata.set_evidence("architecture_alignment", EvidenceType.CODE_DERIVED) activity_detail_metadata.set_evidence("issue_53_alignment", EvidenceType.CODE_DERIVED) register_placeholder(activity_detail_metadata) # ── trainingstyp_verteilung ─────────────────────────────────────────────── trainingstyp_verteilung_metadata = PlaceholderMetadata( key="trainingstyp_verteilung", category="Aktivität", description="Trainingstypen-Verteilung der letzten 14 Tage als JSON", resolver_module="backend/placeholder_resolver.py", resolver_function="_format_trainingstyp_verteilung", data_layer_module=None, data_layer_function=None, source_tables=["activity_log", "training_types"], semantic_contract=( "Liefert eine JSON-Struktur mit der Verteilung der Trainingstypen über 14 Tage. " "Für jeden Trainingstyp: Anzahl Einheiten, Gesamtdauer (Minuten), " "Prozentanteil an Gesamtdauer. Sortiert nach Dauer absteigend." ), business_meaning=( "Analyse-Placeholder für Trainingsvielfalt und -schwerpunkte. " "Erlaubt KI-Prompts, Imbalancen zu erkennen (z.B. nur Kraft, keine Ausdauer) " "oder Zielkonformität zu prüfen (z.B. 'zu wenig Mobilität')." ), unit="json", time_window="14d", output_type=OutputType.JSON, placeholder_type=PlaceholderType.INTERPRETED, format_hint="JSON Object mit Trainingstyp als Key, Value: {count, duration_min, percentage}", example_output=( '{"Krafttraining": {"count": 5, "duration_min": 180, "percentage": 57}, ' '"Ausdauer": {"count": 4, "duration_min": 90, "percentage": 29}, ' '"Mobilität": {"count": 3, "duration_min": 45, "percentage": 14}}' ), minimum_data_requirements=None, quality_filter_policy=None, confidence_logic="Keine Confidence-Berechnung. Aggregation basiert auf verfügbaren Daten.", missing_value_policy=MissingValuePolicy( available=False, value_raw=None, missing_reason="no_data", legacy_display="{}" ), known_limitations=( "OLD RESOLVER PATTERN: Keine Data Layer Funktion. " "Aggregation direkt im Resolver. " "CRITICAL: Keine Qualitätsfilterung - auch ungültige Einheiten werden aggregiert. " "JOIN mit training_types für Typ-Namen. " "EDGE CASE: Einheiten ohne training_type_id werden ignoriert (LEFT JOIN)." ), layer_1_decision="NONE - Old resolver pattern (direct SQL aggregation in resolver)", layer_2a_decision="Placeholder Resolver (aggregation + JSON formatting)", layer_2b_reuse_possible=True, architecture_alignment=( "PARTIALLY ALIGNED: JSON output structure suitable for chart endpoints, " "but no data layer separation. Should be refactored." ), issue_53_alignment="PARTIALLY ALIGNED - output format good, layer separation missing" ) trainingstyp_verteilung_metadata.set_evidence("key", EvidenceType.CODE_DERIVED) trainingstyp_verteilung_metadata.set_evidence("category", EvidenceType.CODE_DERIVED) trainingstyp_verteilung_metadata.set_evidence("description", EvidenceType.DRAFT_DERIVED) trainingstyp_verteilung_metadata.set_evidence("resolver_module", EvidenceType.CODE_DERIVED) trainingstyp_verteilung_metadata.set_evidence("resolver_function", EvidenceType.CODE_DERIVED) trainingstyp_verteilung_metadata.set_evidence("data_layer_module", EvidenceType.CODE_DERIVED) trainingstyp_verteilung_metadata.set_evidence("data_layer_function", EvidenceType.CODE_DERIVED) trainingstyp_verteilung_metadata.set_evidence("source_tables", EvidenceType.CODE_DERIVED) trainingstyp_verteilung_metadata.set_evidence("semantic_contract", EvidenceType.MIXED) trainingstyp_verteilung_metadata.set_evidence("business_meaning", EvidenceType.DRAFT_DERIVED) trainingstyp_verteilung_metadata.set_evidence("unit", EvidenceType.CODE_DERIVED) trainingstyp_verteilung_metadata.set_evidence("time_window", EvidenceType.CODE_DERIVED) trainingstyp_verteilung_metadata.set_evidence("output_type", EvidenceType.CODE_DERIVED) trainingstyp_verteilung_metadata.set_evidence("placeholder_type", EvidenceType.MIXED) trainingstyp_verteilung_metadata.set_evidence("format_hint", EvidenceType.CODE_DERIVED) trainingstyp_verteilung_metadata.set_evidence("example_output", EvidenceType.CODE_DERIVED) trainingstyp_verteilung_metadata.set_evidence("minimum_data_requirements", EvidenceType.UNRESOLVED) trainingstyp_verteilung_metadata.set_evidence("quality_filter_policy", EvidenceType.UNRESOLVED) trainingstyp_verteilung_metadata.set_evidence("confidence_logic", EvidenceType.CODE_DERIVED) trainingstyp_verteilung_metadata.set_evidence("missing_value_policy", EvidenceType.CODE_DERIVED) trainingstyp_verteilung_metadata.set_evidence("known_limitations", EvidenceType.CODE_DERIVED) trainingstyp_verteilung_metadata.set_evidence("layer_1_decision", EvidenceType.CODE_DERIVED) trainingstyp_verteilung_metadata.set_evidence("layer_2a_decision", EvidenceType.CODE_DERIVED) trainingstyp_verteilung_metadata.set_evidence("layer_2b_reuse_possible", EvidenceType.TO_VERIFY) trainingstyp_verteilung_metadata.set_evidence("architecture_alignment", EvidenceType.MIXED) trainingstyp_verteilung_metadata.set_evidence("issue_53_alignment", EvidenceType.MIXED) register_placeholder(trainingstyp_verteilung_metadata) # ============================================================================= # GRUPPE 2: Basic Metrics (7 Placeholders) # ============================================================================= def register_activity_group_2(): """ Register Group 2: Basic Activity Metrics. All use Phase 0c Data Layer architecture. """ # Common metadata for most Group 2 placeholders common_metadata_7d = { "category": "Aktivität", "resolver_module": "backend/placeholder_resolver.py", "data_layer_module": "backend/data_layer/activity_metrics.py", "source_tables": ["activity_log"], "time_window": "7d", "output_type": OutputType.NUMERIC, "placeholder_type": PlaceholderType.INTERPRETED, "confidence_logic": "datenpunktbasierte Coverage-Logik (calculate_confidence in data layer)", "missing_value_policy": MissingValuePolicy( available=False, value_raw=None, missing_reason="insufficient_data", legacy_display="nicht genug Daten" ), "layer_1_decision": "Data Layer (activity_metrics.py)", "layer_2a_decision": "Placeholder Resolver (formatting only)", "architecture_alignment": "Phase 0c Multi-Layer Architecture conform", "issue_53_alignment": "Layer separation established" } # Common evidence for Group 2 placeholders common_evidence_7d = { "category": EvidenceType.CODE_DERIVED, "resolver_module": EvidenceType.CODE_DERIVED, "data_layer_module": EvidenceType.CODE_DERIVED, "source_tables": EvidenceType.CODE_DERIVED, "time_window": EvidenceType.CODE_DERIVED, "output_type": EvidenceType.CODE_DERIVED, "placeholder_type": EvidenceType.MIXED, "confidence_logic": EvidenceType.CODE_DERIVED, "missing_value_policy": EvidenceType.CODE_DERIVED, "layer_1_decision": EvidenceType.CODE_DERIVED, "layer_2a_decision": EvidenceType.CODE_DERIVED, "layer_2b_reuse_possible": EvidenceType.TO_VERIFY, "architecture_alignment": EvidenceType.CODE_DERIVED, "issue_53_alignment": EvidenceType.CODE_DERIVED, "minimum_data_requirements": EvidenceType.UNRESOLVED, "quality_filter_policy": EvidenceType.UNRESOLVED } # ── training_minutes_week ───────────────────────────────────────────────── training_minutes_week_metadata = PlaceholderMetadata( key="training_minutes_week", description="Gesamte Trainingsminuten in 7 Tagen", resolver_function="get_training_minutes_week", data_layer_function="get_weekly_training_volume", semantic_contract=( "Liefert die Summe aller Trainingsminuten über die letzten 7 Tage. " "Basiert auf activity_log.duration_minutes. Keine Qualitätsfilterung - " "alle Einheiten werden summiert unabhängig von Dauer oder Intensität." ), business_meaning=( "Kernmetrik für Trainingsvolumen und Periodisierung. " "Erlaubt Erkennung von Übertraining (zu hoch), Detraining (zu niedrig), " "und Trendanalysen (Volumen steigend/fallend)." ), unit="Minuten", format_hint="Ganzzahl", example_output="315", known_limitations=( "Keine Qualitätsfilterung. 1-Minuten-Einheiten zählen gleich wie " "60-Minuten-Sessions. Keine Intensitätsgewichtung. " "EDGE CASE: duration_minutes = 0 oder NULL werden als 0 gezählt." ), layer_2b_reuse_possible=True, **common_metadata_7d ) training_minutes_week_metadata.evidence.update(common_evidence_7d) training_minutes_week_metadata.set_evidence("key", EvidenceType.CODE_DERIVED) training_minutes_week_metadata.set_evidence("description", EvidenceType.DRAFT_DERIVED) training_minutes_week_metadata.set_evidence("resolver_function", EvidenceType.CODE_DERIVED) training_minutes_week_metadata.set_evidence("data_layer_function", EvidenceType.CODE_DERIVED) training_minutes_week_metadata.set_evidence("semantic_contract", EvidenceType.MIXED) training_minutes_week_metadata.set_evidence("business_meaning", EvidenceType.DRAFT_DERIVED) training_minutes_week_metadata.set_evidence("unit", EvidenceType.CODE_DERIVED) training_minutes_week_metadata.set_evidence("format_hint", EvidenceType.CODE_DERIVED) training_minutes_week_metadata.set_evidence("example_output", EvidenceType.CODE_DERIVED) training_minutes_week_metadata.set_evidence("known_limitations", EvidenceType.CODE_DERIVED) register_placeholder(training_minutes_week_metadata) # ── training_frequency_7d ───────────────────────────────────────────────── training_frequency_7d_metadata = PlaceholderMetadata( key="training_frequency_7d", description="Anzahl Trainingseinheiten in 7 Tagen", resolver_function="get_training_frequency_7d", data_layer_function="get_training_frequency", semantic_contract=( "Liefert die Anzahl der Trainingseinheiten über die letzten 7 Tage. " "Basiert auf COUNT(activity_log.id). Keine Qualitätsfilterung - " "jede Einheit wird gezählt unabhängig von Dauer oder Intensität." ), business_meaning=( "Frequenz-Metrik für Trainingsregelmäßigkeit. " "Erlaubt Erkennung von Konsistenz (z.B. 4-5 Einheiten/Woche), " "Pausentagen, und Trainingszyklen." ), unit="Anzahl Einheiten", format_hint="Ganzzahl", example_output="5", known_limitations=( "Keine Qualitätsfilterung. 1-Minuten-Einheiten zählen gleich wie " "60-Minuten-Sessions. " "EDGE CASE: Mehrere Einheiten am selben Tag werden separat gezählt " "(kein Tages-Grouping)." ), layer_2b_reuse_possible=True, **common_metadata_7d ) training_frequency_7d_metadata.evidence.update(common_evidence_7d) training_frequency_7d_metadata.set_evidence("key", EvidenceType.CODE_DERIVED) training_frequency_7d_metadata.set_evidence("description", EvidenceType.DRAFT_DERIVED) training_frequency_7d_metadata.set_evidence("resolver_function", EvidenceType.CODE_DERIVED) training_frequency_7d_metadata.set_evidence("data_layer_function", EvidenceType.CODE_DERIVED) training_frequency_7d_metadata.set_evidence("semantic_contract", EvidenceType.MIXED) training_frequency_7d_metadata.set_evidence("business_meaning", EvidenceType.DRAFT_DERIVED) training_frequency_7d_metadata.set_evidence("unit", EvidenceType.CODE_DERIVED) training_frequency_7d_metadata.set_evidence("format_hint", EvidenceType.CODE_DERIVED) training_frequency_7d_metadata.set_evidence("example_output", EvidenceType.CODE_DERIVED) training_frequency_7d_metadata.set_evidence("known_limitations", EvidenceType.CODE_DERIVED) register_placeholder(training_frequency_7d_metadata) # ── quality_sessions_pct ────────────────────────────────────────────────── # Custom metadata for quality_sessions_pct (28d time window, different source tables) quality_metadata_custom = {**common_metadata_7d} quality_metadata_custom["time_window"] = "28d" quality_metadata_custom["source_tables"] = ["activity_log", "training_types"] quality_sessions_pct_metadata = PlaceholderMetadata( key="quality_sessions_pct", description="Prozentsatz Qualitäts-Sessions in 28 Tagen", resolver_function="get_quality_sessions_pct", data_layer_function="calculate_quality_sessions_percentage", semantic_contract=( "Liefert den Prozentsatz der Trainingseinheiten, die als 'Qualitäts-Sessions' " "klassifiziert werden (duration >= 30 min UND intensity >= moderate). " "Berechnung: (quality_sessions / total_sessions) × 100." ), business_meaning=( "Qualitäts-Metrik für Trainingseffektivität. " "Niedrige Werte (<50%) deuten auf zu viele kurze/low-intensity Sessions. " "Hohe Werte (>80%) deuten auf konsequent forderndes Training." ), unit="Prozent", format_hint="Ganzzahl 0-100", example_output="68", known_limitations=( "CRITICAL TO_VERIFY: Code nutzt quality_label Feld (may not exist). " "Canonical expects duration >= 30min + intensity >= moderate. " "MISMATCH: quality_label vs. calculated quality. " "EDGE CASE: Einheiten ohne training_type_id werden ignoriert." ), layer_2b_reuse_possible=True, **quality_metadata_custom ) quality_sessions_pct_metadata.evidence.update(common_evidence_7d) quality_sessions_pct_metadata.evidence["time_window"] = EvidenceType.CODE_DERIVED quality_sessions_pct_metadata.set_evidence("key", EvidenceType.CODE_DERIVED) quality_sessions_pct_metadata.set_evidence("description", EvidenceType.DRAFT_DERIVED) quality_sessions_pct_metadata.set_evidence("resolver_function", EvidenceType.CODE_DERIVED) quality_sessions_pct_metadata.set_evidence("data_layer_function", EvidenceType.CODE_DERIVED) quality_sessions_pct_metadata.set_evidence("semantic_contract", EvidenceType.TO_VERIFY) quality_sessions_pct_metadata.set_evidence("business_meaning", EvidenceType.DRAFT_DERIVED) quality_sessions_pct_metadata.set_evidence("unit", EvidenceType.CODE_DERIVED) quality_sessions_pct_metadata.set_evidence("format_hint", EvidenceType.CODE_DERIVED) quality_sessions_pct_metadata.set_evidence("example_output", EvidenceType.CODE_DERIVED) quality_sessions_pct_metadata.set_evidence("known_limitations", EvidenceType.CODE_DERIVED) quality_sessions_pct_metadata.set_evidence("source_tables", EvidenceType.CODE_DERIVED) register_placeholder(quality_sessions_pct_metadata) # ── proxy_internal_load_7d ──────────────────────────────────────────────── # Custom metadata for proxy_internal_load (different source tables) proxy_load_metadata_custom = {**common_metadata_7d} proxy_load_metadata_custom["source_tables"] = ["activity_log", "training_types"] proxy_internal_load_7d_metadata = PlaceholderMetadata( key="proxy_internal_load_7d", description="Proxy Internal Load über 7 Tage", resolver_function="get_proxy_internal_load_7d", data_layer_function="calculate_proxy_internal_load", semantic_contract=( "Liefert einen Proxy-Wert für die interne Trainingsbelastung über 7 Tage. " "Berechnet als: Summe(duration × intensity_factor × quality_factor). " "Intensity_factor basiert auf RPE-Mapping (1-10 → 0.1-1.0). " "Quality_factor basiert auf quality_label (easy/moderate/hard → 0.8/1.0/1.2)." ), business_meaning=( "Load-Monitoring-Metrik für Periodisierung und Übertrainings-Prävention. " "Erlaubt Vergleich interner Belastung über Zeit (nicht nur Volumen). " "Basis für Monotony und Strain Scores." ), unit="Load-Punkte", format_hint="Ganzzahl", example_output="1850", known_limitations=( "PROXY-WERT: Nicht trainings wissenschaftlich validiert. " "RPE-Mapping vereinfacht (linear 1-10 → 0.1-1.0). " "CRITICAL BUG: RPE 4-5 maps to 'moderate' quality but 'moderate' " "not in quality_factors dict (easy/hard only). " "EDGE CASE: Einheiten ohne RPE bekommen intensity_factor = 0.5 (default)." ), layer_2b_reuse_possible=True, **proxy_load_metadata_custom ) proxy_internal_load_7d_metadata.evidence.update(common_evidence_7d) proxy_internal_load_7d_metadata.set_evidence("key", EvidenceType.CODE_DERIVED) proxy_internal_load_7d_metadata.set_evidence("description", EvidenceType.DRAFT_DERIVED) proxy_internal_load_7d_metadata.set_evidence("resolver_function", EvidenceType.CODE_DERIVED) proxy_internal_load_7d_metadata.set_evidence("data_layer_function", EvidenceType.CODE_DERIVED) proxy_internal_load_7d_metadata.set_evidence("semantic_contract", EvidenceType.CODE_DERIVED) proxy_internal_load_7d_metadata.set_evidence("business_meaning", EvidenceType.DRAFT_DERIVED) proxy_internal_load_7d_metadata.set_evidence("unit", EvidenceType.DRAFT_DERIVED) proxy_internal_load_7d_metadata.set_evidence("format_hint", EvidenceType.CODE_DERIVED) proxy_internal_load_7d_metadata.set_evidence("example_output", EvidenceType.CODE_DERIVED) proxy_internal_load_7d_metadata.set_evidence("known_limitations", EvidenceType.CODE_DERIVED) proxy_internal_load_7d_metadata.set_evidence("source_tables", EvidenceType.CODE_DERIVED) register_placeholder(proxy_internal_load_7d_metadata) # ── monotony_score ──────────────────────────────────────────────────────── monotony_score_metadata = PlaceholderMetadata( key="monotony_score", description="Trainings-Monotonie-Score über 7 Tage", resolver_function="get_monotony_score", data_layer_function="calculate_monotony_score", semantic_contract=( "Liefert einen Monotonie-Score für die Trainingsbelastung über 7 Tage. " "Berechnet als: mean(daily_duration) / std_dev(daily_duration). " "Hohe Werte (>2.0) = monotones Training (gleichbleibende Belastung). " "Niedrige Werte (<1.5) = variable Belastung." ), business_meaning=( "Variabilitäts-Metrik für Periodisierung. " "Hohe Monotonie erhöht Übertrainings-Risiko (Foster et al. 2001). " "Kombination mit Strain Score für Übertrainings-Warnung." ), unit="Score (dimensionslos)", format_hint="Dezimalzahl, typisch 0.5-3.0", example_output="1.8", known_limitations=( "FORMULA: mean / std_dev. " "EDGE CASE: std_dev = 0 (alle Tage gleich) → division by zero → return mean as fallback. " "EDGE CASE: < 3 Trainingstage → nicht aussagekräftig, aber wird trotzdem berechnet. " "ZEITFENSTER: 7 Tage möglicherweise zu kurz für verlässliche Monotonie-Analyse " "(Foster original: 28 Tage)." ), layer_2b_reuse_possible=True, **common_metadata_7d ) monotony_score_metadata.evidence.update(common_evidence_7d) monotony_score_metadata.set_evidence("key", EvidenceType.CODE_DERIVED) monotony_score_metadata.set_evidence("description", EvidenceType.DRAFT_DERIVED) monotony_score_metadata.set_evidence("resolver_function", EvidenceType.CODE_DERIVED) monotony_score_metadata.set_evidence("data_layer_function", EvidenceType.CODE_DERIVED) monotony_score_metadata.set_evidence("semantic_contract", EvidenceType.CODE_DERIVED) monotony_score_metadata.set_evidence("business_meaning", EvidenceType.DRAFT_DERIVED) monotony_score_metadata.set_evidence("unit", EvidenceType.DRAFT_DERIVED) monotony_score_metadata.set_evidence("format_hint", EvidenceType.CODE_DERIVED) monotony_score_metadata.set_evidence("example_output", EvidenceType.CODE_DERIVED) monotony_score_metadata.set_evidence("known_limitations", EvidenceType.CODE_DERIVED) register_placeholder(monotony_score_metadata) # ── strain_score ────────────────────────────────────────────────────────── strain_score_metadata = PlaceholderMetadata( key="strain_score", description="Trainings-Strain-Score über 7 Tage", resolver_function="get_strain_score", data_layer_function="calculate_strain_score", semantic_contract=( "Liefert einen Strain-Score für die Gesamtbelastung über 7 Tage. " "Berechnet als: proxy_internal_load_7d × monotony_score. " "Kombiniert Volumen (Load) mit Variabilität (Monotonie) zu Gesamt-Strain." ), business_meaning=( "Übertrainings-Risiko-Metrik (Foster et al. 2001). " "Hohe Werte = hohes Risiko (hohe Last + monotones Training). " "Schwellenwerte: <5000 = moderat, 5000-8000 = erhöht, >8000 = kritisch." ), unit="Strain-Punkte", format_hint="Ganzzahl, typisch 1000-10000", example_output="3330", known_limitations=( "FORMULA: load × monotony. " "ABHÄNGIG von proxy_internal_load_7d (bereits Proxy-Wert). " "SCHWELLENWERTE: Nicht wissenschaftlich validiert für diese App. " "EDGE CASE: Wenn monotony_score Fallback-Logik greift (std_dev=0), " "wird Strain = Load × mean (nicht original Formel)." ), layer_2b_reuse_possible=True, **common_metadata_7d ) strain_score_metadata.evidence.update(common_evidence_7d) strain_score_metadata.set_evidence("key", EvidenceType.CODE_DERIVED) strain_score_metadata.set_evidence("description", EvidenceType.DRAFT_DERIVED) strain_score_metadata.set_evidence("resolver_function", EvidenceType.CODE_DERIVED) strain_score_metadata.set_evidence("data_layer_function", EvidenceType.CODE_DERIVED) strain_score_metadata.set_evidence("semantic_contract", EvidenceType.CODE_DERIVED) strain_score_metadata.set_evidence("business_meaning", EvidenceType.DRAFT_DERIVED) strain_score_metadata.set_evidence("unit", EvidenceType.DRAFT_DERIVED) strain_score_metadata.set_evidence("format_hint", EvidenceType.DRAFT_DERIVED) strain_score_metadata.set_evidence("example_output", EvidenceType.CODE_DERIVED) strain_score_metadata.set_evidence("known_limitations", EvidenceType.CODE_DERIVED) register_placeholder(strain_score_metadata) # ── rest_day_compliance ─────────────────────────────────────────────────── # Custom metadata for rest_day_compliance (28d time window, different source tables) rest_metadata_custom = {**common_metadata_7d} rest_metadata_custom["time_window"] = "28d" rest_metadata_custom["source_tables"] = ["rest_days", "activity_log"] rest_day_compliance_metadata = PlaceholderMetadata( key="rest_day_compliance", description="Ruhetag-Compliance über 28 Tage", resolver_function="get_rest_day_compliance", data_layer_function="calculate_rest_day_compliance", semantic_contract=( "Liefert den Prozentsatz der geplanten Ruhetage, die tatsächlich eingehalten wurden. " "Berechnung: (eingehaltene_ruhetage / geplante_ruhetage) × 100. " "Ruhetag = Eintrag in rest_days OHNE korrespondierenden activity_log Eintrag am selben Tag." ), business_meaning=( "Regenerations-Compliance-Metrik. " "Niedrige Werte (<70%) = schlechte Regeneration, Übertrainings-Risiko. " "Hohe Werte (>90%) = gute Disziplin bei Regeneration." ), unit="Prozent", format_hint="Ganzzahl 0-100", example_output="85", known_limitations=( "JSONB DEPENDENCY: Nutzt rest_config->>'focus' JSONB Feld. " "EDGE CASE: rest_config NULL → Ruhetag wird ignoriert (nicht gezählt). " "EDGE CASE: Kein activity_log Eintrag am Ruhetag = compliant (korrekt). " "EDGE CASE: Mehrere activity_log Einträge am Ruhetag = alle zählen als violation." ), layer_2b_reuse_possible=False, **rest_metadata_custom ) rest_day_compliance_metadata.evidence.update(common_evidence_7d) rest_day_compliance_metadata.evidence["time_window"] = EvidenceType.CODE_DERIVED rest_day_compliance_metadata.set_evidence("key", EvidenceType.CODE_DERIVED) rest_day_compliance_metadata.set_evidence("description", EvidenceType.DRAFT_DERIVED) rest_day_compliance_metadata.set_evidence("resolver_function", EvidenceType.CODE_DERIVED) rest_day_compliance_metadata.set_evidence("data_layer_function", EvidenceType.CODE_DERIVED) rest_day_compliance_metadata.set_evidence("semantic_contract", EvidenceType.MIXED) rest_day_compliance_metadata.set_evidence("business_meaning", EvidenceType.DRAFT_DERIVED) rest_day_compliance_metadata.set_evidence("unit", EvidenceType.CODE_DERIVED) rest_day_compliance_metadata.set_evidence("format_hint", EvidenceType.CODE_DERIVED) rest_day_compliance_metadata.set_evidence("example_output", EvidenceType.CODE_DERIVED) rest_day_compliance_metadata.set_evidence("known_limitations", EvidenceType.CODE_DERIVED) rest_day_compliance_metadata.set_evidence("source_tables", EvidenceType.CODE_DERIVED) rest_day_compliance_metadata.set_evidence("layer_2b_reuse_possible", EvidenceType.CODE_DERIVED) register_placeholder(rest_day_compliance_metadata) # ============================================================================= # GRUPPE 3: Advanced Metrics (7 Placeholders) # ============================================================================= def register_activity_group_3(): """ Register Group 3: Advanced Activity Metrics. Includes ability balance metrics (5), VO2 Max trend, and activity score. """ # Common metadata for ability_balance placeholders common_metadata_ability = { "category": "Aktivität", "resolver_module": "backend/placeholder_resolver.py", "data_layer_module": "backend/data_layer/activity_metrics.py", "data_layer_function": "calculate_ability_balance", "source_tables": ["activity_log", "training_types"], "time_window": "28d", "output_type": OutputType.NUMERIC, "placeholder_type": PlaceholderType.SCORE, "unit": "Score (0-100)", "format_hint": "Ganzzahl 0-100", "confidence_logic": "datenpunktbasierte Coverage-Logik (calculate_confidence in data layer)", "missing_value_policy": MissingValuePolicy( available=False, value_raw=None, missing_reason="insufficient_data", legacy_display="nicht genug Daten" ), "layer_1_decision": "Data Layer (activity_metrics.calculate_ability_balance)", "layer_2a_decision": "Placeholder Resolver (extract specific ability from dict)", "architecture_alignment": "Phase 0c Multi-Layer Architecture conform", "issue_53_alignment": "Layer separation established" } # Common evidence for ability_balance placeholders common_evidence_ability = { "category": EvidenceType.CODE_DERIVED, "resolver_module": EvidenceType.CODE_DERIVED, "data_layer_module": EvidenceType.CODE_DERIVED, "data_layer_function": EvidenceType.CODE_DERIVED, "source_tables": EvidenceType.CODE_DERIVED, "time_window": EvidenceType.CODE_DERIVED, "output_type": EvidenceType.CODE_DERIVED, "placeholder_type": EvidenceType.MIXED, "unit": EvidenceType.CODE_DERIVED, "format_hint": EvidenceType.CODE_DERIVED, "confidence_logic": EvidenceType.CODE_DERIVED, "missing_value_policy": EvidenceType.CODE_DERIVED, "layer_1_decision": EvidenceType.CODE_DERIVED, "layer_2a_decision": EvidenceType.CODE_DERIVED, "layer_2b_reuse_possible": EvidenceType.TO_VERIFY, "architecture_alignment": EvidenceType.CODE_DERIVED, "issue_53_alignment": EvidenceType.CODE_DERIVED, "minimum_data_requirements": EvidenceType.UNRESOLVED, "quality_filter_policy": EvidenceType.UNRESOLVED } # ── ability_balance_strength ────────────────────────────────────────────── ability_balance_strength_metadata = PlaceholderMetadata( key="ability_balance_strength", description="Fähigkeiten-Balance-Score: Kraft (28d)", resolver_function="get_ability_balance_strength", semantic_contract=( "Liefert einen normierten Score (0-100) für die relative Trainingsbelastung " "der Fähigkeit 'Kraft' über 28 Tage. Berechnung: " "(kraft_load / max_load_aller_fähigkeiten) × 100. " "Basiert auf training_types.abilities JSONB Feld (strength_percentage)." ), business_meaning=( "Balance-Metrik für Trainingsvielfalt. " "Zeigt ob Kraft überproportional (>80), balanciert (40-60), oder unterrepräsentiert (<20) trainiert wird. " "Vergleich mit anderen Fähigkeiten zeigt Imbalancen." ), example_output="75", known_limitations=( "JSONB DEPENDENCY: training_types.abilities JSONB Feld muss populated sein. " "FORMULA: max-normalized (nicht prozentual vom Total). " "Höchste Fähigkeit = 100, andere relativ dazu. " "EDGE CASE: abilities NULL → Trainingstyp wird mit 0% für alle Fähigkeiten gewertet. " "EDGE CASE: Alle Fähigkeiten = 0 (keine Trainings) → division by zero → return 0." ), layer_2b_reuse_possible=True, **common_metadata_ability ) ability_balance_strength_metadata.evidence.update(common_evidence_ability) ability_balance_strength_metadata.set_evidence("key", EvidenceType.CODE_DERIVED) ability_balance_strength_metadata.set_evidence("description", EvidenceType.DRAFT_DERIVED) ability_balance_strength_metadata.set_evidence("resolver_function", EvidenceType.CODE_DERIVED) ability_balance_strength_metadata.set_evidence("semantic_contract", EvidenceType.CODE_DERIVED) ability_balance_strength_metadata.set_evidence("business_meaning", EvidenceType.DRAFT_DERIVED) ability_balance_strength_metadata.set_evidence("example_output", EvidenceType.CODE_DERIVED) ability_balance_strength_metadata.set_evidence("known_limitations", EvidenceType.CODE_DERIVED) register_placeholder(ability_balance_strength_metadata) # ── ability_balance_endurance ───────────────────────────────────────────── ability_balance_endurance_metadata = PlaceholderMetadata( key="ability_balance_endurance", description="Fähigkeiten-Balance-Score: Ausdauer (28d)", resolver_function="get_ability_balance_endurance", semantic_contract=( "Liefert einen normierten Score (0-100) für die relative Trainingsbelastung " "der Fähigkeit 'Ausdauer' über 28 Tage. Berechnung: " "(ausdauer_load / max_load_aller_fähigkeiten) × 100. " "Basiert auf training_types.abilities JSONB Feld (endurance_percentage)." ), business_meaning=( "Balance-Metrik für Trainingsvielfalt. " "Zeigt ob Ausdauer überproportional (>80), balanciert (40-60), oder unterrepräsentiert (<20) trainiert wird." ), example_output="92", known_limitations=( "JSONB DEPENDENCY: training_types.abilities JSONB Feld muss populated sein. " "FORMULA: max-normalized (nicht prozentual vom Total). " "EDGE CASE: abilities NULL → 0% Ausdauer. " "EDGE CASE: Alle Fähigkeiten = 0 → return 0." ), layer_2b_reuse_possible=True, **common_metadata_ability ) ability_balance_endurance_metadata.evidence.update(common_evidence_ability) ability_balance_endurance_metadata.set_evidence("key", EvidenceType.CODE_DERIVED) ability_balance_endurance_metadata.set_evidence("description", EvidenceType.DRAFT_DERIVED) ability_balance_endurance_metadata.set_evidence("resolver_function", EvidenceType.CODE_DERIVED) ability_balance_endurance_metadata.set_evidence("semantic_contract", EvidenceType.CODE_DERIVED) ability_balance_endurance_metadata.set_evidence("business_meaning", EvidenceType.DRAFT_DERIVED) ability_balance_endurance_metadata.set_evidence("example_output", EvidenceType.CODE_DERIVED) ability_balance_endurance_metadata.set_evidence("known_limitations", EvidenceType.CODE_DERIVED) register_placeholder(ability_balance_endurance_metadata) # ── ability_balance_mental ──────────────────────────────────────────────── ability_balance_mental_metadata = PlaceholderMetadata( key="ability_balance_mental", description="Fähigkeiten-Balance-Score: Geist & Meditation (28d)", resolver_function="get_ability_balance_mental", semantic_contract=( "Liefert einen normierten Score (0-100) für die relative Trainingsbelastung " "der Fähigkeit 'Geist & Meditation' über 28 Tage. Berechnung: " "(mental_load / max_load_aller_fähigkeiten) × 100. " "Basiert auf training_types.abilities JSONB Feld (mental_percentage)." ), business_meaning=( "Balance-Metrik für ganzheitliches Training. " "Zeigt ob mentale/meditative Praxis im Verhältnis zu physischem Training steht. " "Oft niedrig (<20) bei rein physisch orientierten Trainern." ), example_output="15", known_limitations=( "JSONB DEPENDENCY: training_types.abilities JSONB Feld muss populated sein. " "FORMULA: max-normalized (nicht prozentual vom Total). " "EDGE CASE: abilities NULL → 0% Mental. " "EDGE CASE: Alle Fähigkeiten = 0 → return 0." ), layer_2b_reuse_possible=True, **common_metadata_ability ) ability_balance_mental_metadata.evidence.update(common_evidence_ability) ability_balance_mental_metadata.set_evidence("key", EvidenceType.CODE_DERIVED) ability_balance_mental_metadata.set_evidence("description", EvidenceType.DRAFT_DERIVED) ability_balance_mental_metadata.set_evidence("resolver_function", EvidenceType.CODE_DERIVED) ability_balance_mental_metadata.set_evidence("semantic_contract", EvidenceType.CODE_DERIVED) ability_balance_mental_metadata.set_evidence("business_meaning", EvidenceType.DRAFT_DERIVED) ability_balance_mental_metadata.set_evidence("example_output", EvidenceType.CODE_DERIVED) ability_balance_mental_metadata.set_evidence("known_limitations", EvidenceType.CODE_DERIVED) register_placeholder(ability_balance_mental_metadata) # ── ability_balance_coordination ────────────────────────────────────────── ability_balance_coordination_metadata = PlaceholderMetadata( key="ability_balance_coordination", description="Fähigkeiten-Balance-Score: Koordination (28d)", resolver_function="get_ability_balance_coordination", semantic_contract=( "Liefert einen normierten Score (0-100) für die relative Trainingsbelastung " "der Fähigkeit 'Koordination' über 28 Tage. Berechnung: " "(koordination_load / max_load_aller_fähigkeiten) × 100. " "Basiert auf training_types.abilities JSONB Feld (coordination_percentage)." ), business_meaning=( "Balance-Metrik für motorische Vielfalt. " "Zeigt ob Koordinations-Training (Kampfsport, Tanz, komplexe Bewegungsmuster) " "im Trainingsplan vertreten ist. Oft niedrig (<20) bei reinem Kraft/Ausdauer-Training." ), example_output="28", known_limitations=( "JSONB DEPENDENCY: training_types.abilities JSONB Feld muss populated sein. " "FORMULA: max-normalized (nicht prozentual vom Total). " "EDGE CASE: abilities NULL → 0% Koordination. " "EDGE CASE: Alle Fähigkeiten = 0 → return 0." ), layer_2b_reuse_possible=True, **common_metadata_ability ) ability_balance_coordination_metadata.evidence.update(common_evidence_ability) ability_balance_coordination_metadata.set_evidence("key", EvidenceType.CODE_DERIVED) ability_balance_coordination_metadata.set_evidence("description", EvidenceType.DRAFT_DERIVED) ability_balance_coordination_metadata.set_evidence("resolver_function", EvidenceType.CODE_DERIVED) ability_balance_coordination_metadata.set_evidence("semantic_contract", EvidenceType.CODE_DERIVED) ability_balance_coordination_metadata.set_evidence("business_meaning", EvidenceType.DRAFT_DERIVED) ability_balance_coordination_metadata.set_evidence("example_output", EvidenceType.CODE_DERIVED) ability_balance_coordination_metadata.set_evidence("known_limitations", EvidenceType.CODE_DERIVED) register_placeholder(ability_balance_coordination_metadata) # ── ability_balance_mobility ────────────────────────────────────────────── ability_balance_mobility_metadata = PlaceholderMetadata( key="ability_balance_mobility", description="Fähigkeiten-Balance-Score: Mobilität (28d)", resolver_function="get_ability_balance_mobility", semantic_contract=( "Liefert einen normierten Score (0-100) für die relative Trainingsbelastung " "der Fähigkeit 'Mobilität' über 28 Tage. Berechnung: " "(mobilität_load / max_load_aller_fähigkeiten) × 100. " "Basiert auf training_types.abilities JSONB Feld (mobility_percentage)." ), business_meaning=( "Balance-Metrik für Beweglichkeits-Training. " "Zeigt ob Mobilität (Stretching, Yoga, Fasziendehnung) im Trainingsplan vertreten ist. " "Wichtig für Verletzungsprävention und Regeneration. Oft vernachlässigt (<20)." ), example_output="22", known_limitations=( "JSONB DEPENDENCY: training_types.abilities JSONB Feld muss populated sein. " "FORMULA: max-normalized (nicht prozentual vom Total). " "EDGE CASE: abilities NULL → 0% Mobilität. " "EDGE CASE: Alle Fähigkeiten = 0 → return 0." ), layer_2b_reuse_possible=True, **common_metadata_ability ) ability_balance_mobility_metadata.evidence.update(common_evidence_ability) ability_balance_mobility_metadata.set_evidence("key", EvidenceType.CODE_DERIVED) ability_balance_mobility_metadata.set_evidence("description", EvidenceType.DRAFT_DERIVED) ability_balance_mobility_metadata.set_evidence("resolver_function", EvidenceType.CODE_DERIVED) ability_balance_mobility_metadata.set_evidence("semantic_contract", EvidenceType.CODE_DERIVED) ability_balance_mobility_metadata.set_evidence("business_meaning", EvidenceType.DRAFT_DERIVED) ability_balance_mobility_metadata.set_evidence("example_output", EvidenceType.CODE_DERIVED) ability_balance_mobility_metadata.set_evidence("known_limitations", EvidenceType.CODE_DERIVED) register_placeholder(ability_balance_mobility_metadata) # ── vo2max_trend_28d ────────────────────────────────────────────────────── vo2max_trend_28d_metadata = PlaceholderMetadata( key="vo2max_trend_28d", description="VO2 Max Trend über 28 Tage", category="Aktivität", resolver_module="backend/placeholder_resolver.py", resolver_function="get_vo2max_trend_28d", data_layer_module="backend/data_layer/activity_metrics.py", data_layer_function="calculate_vo2max_trend", source_tables=["vitals_baseline"], time_window="28d", output_type=OutputType.NUMERIC, placeholder_type=PlaceholderType.INTERPRETED, semantic_contract=( "Liefert den Trend der VO2 Max Werte über 28 Tage als Delta (letzter - erster Wert). " "Positiver Wert = Verbesserung der aeroben Fitness, " "negativer Wert = Verschlechterung. " "Basiert auf vitals_baseline.vo2_max (gemessen oder geschätzt)." ), business_meaning=( "Fitness-Trend-Metrik für aerobe Kapazität. " "Indikator für Trainingseffektivität bei Ausdauer-fokussiertem Training. " "QUESTIONABLE CATEGORY: VO2 Max ist primär Vitalwert/Recovery, nicht Activity." ), unit="ml/kg/min (Delta)", format_hint="Dezimalzahl mit Vorzeichen, typisch -3.0 bis +3.0", example_output="+1.2", minimum_data_requirements=None, quality_filter_policy=None, confidence_logic=( "datenpunktbasierte Coverage-Logik. " "Mindestens 2 VO2 Max Messungen in 28d erforderlich für Trend." ), missing_value_policy=MissingValuePolicy( available=False, value_raw=None, missing_reason="insufficient_data", legacy_display="nicht genug Daten" ), known_limitations=( "QUESTIONABLE CATEGORY: VO2 Max primär Recovery-Cluster, nicht Activity-Cluster. " "DEPENDENCY: vitals_baseline.vo2_max Feld (oft NULL wenn nicht gemessen). " "EDGE CASE: Nur 1 Messung → kein Trend → missing_value. " "EDGE CASE: Große Zeitlücken zwischen Messungen → Trend nicht aussagekräftig." ), layer_1_decision="Data Layer (activity_metrics.calculate_vo2max_trend) - QUESTIONABLE", layer_2a_decision="Placeholder Resolver (formatting only)", layer_2b_reuse_possible=True, architecture_alignment="Phase 0c Multi-Layer Architecture conform", issue_53_alignment="Layer separation established" ) vo2max_trend_28d_metadata.set_evidence("key", EvidenceType.CODE_DERIVED) vo2max_trend_28d_metadata.set_evidence("category", EvidenceType.TO_VERIFY) vo2max_trend_28d_metadata.set_evidence("description", EvidenceType.DRAFT_DERIVED) vo2max_trend_28d_metadata.set_evidence("resolver_module", EvidenceType.CODE_DERIVED) vo2max_trend_28d_metadata.set_evidence("resolver_function", EvidenceType.CODE_DERIVED) vo2max_trend_28d_metadata.set_evidence("data_layer_module", EvidenceType.CODE_DERIVED) vo2max_trend_28d_metadata.set_evidence("data_layer_function", EvidenceType.CODE_DERIVED) vo2max_trend_28d_metadata.set_evidence("source_tables", EvidenceType.CODE_DERIVED) vo2max_trend_28d_metadata.set_evidence("time_window", EvidenceType.CODE_DERIVED) vo2max_trend_28d_metadata.set_evidence("output_type", EvidenceType.CODE_DERIVED) vo2max_trend_28d_metadata.set_evidence("placeholder_type", EvidenceType.MIXED) vo2max_trend_28d_metadata.set_evidence("semantic_contract", EvidenceType.MIXED) vo2max_trend_28d_metadata.set_evidence("business_meaning", EvidenceType.DRAFT_DERIVED) vo2max_trend_28d_metadata.set_evidence("unit", EvidenceType.CODE_DERIVED) vo2max_trend_28d_metadata.set_evidence("format_hint", EvidenceType.DRAFT_DERIVED) vo2max_trend_28d_metadata.set_evidence("example_output", EvidenceType.CODE_DERIVED) vo2max_trend_28d_metadata.set_evidence("minimum_data_requirements", EvidenceType.UNRESOLVED) vo2max_trend_28d_metadata.set_evidence("quality_filter_policy", EvidenceType.UNRESOLVED) vo2max_trend_28d_metadata.set_evidence("confidence_logic", EvidenceType.CODE_DERIVED) vo2max_trend_28d_metadata.set_evidence("missing_value_policy", EvidenceType.CODE_DERIVED) vo2max_trend_28d_metadata.set_evidence("known_limitations", EvidenceType.MIXED) vo2max_trend_28d_metadata.set_evidence("layer_1_decision", EvidenceType.CODE_DERIVED) vo2max_trend_28d_metadata.set_evidence("layer_2a_decision", EvidenceType.CODE_DERIVED) vo2max_trend_28d_metadata.set_evidence("layer_2b_reuse_possible", EvidenceType.TO_VERIFY) vo2max_trend_28d_metadata.set_evidence("architecture_alignment", EvidenceType.CODE_DERIVED) vo2max_trend_28d_metadata.set_evidence("issue_53_alignment", EvidenceType.CODE_DERIVED) register_placeholder(vo2max_trend_28d_metadata) # ── activity_score ──────────────────────────────────────────────────────── activity_score_metadata = PlaceholderMetadata( key="activity_score", description="Gesamtaktivitäts-Score (gewichtet)", category="Aktivität", resolver_module="backend/placeholder_resolver.py", resolver_function="get_activity_score", data_layer_module="backend/data_layer/scores.py", data_layer_function="calculate_activity_score", source_tables=["activity_log", "training_types", "rest_days", "vitals_baseline", "user_focus_area_weights"], time_window="composite (7d, 14d, 28d mixed)", output_type=OutputType.NUMERIC, placeholder_type=PlaceholderType.SCORE, semantic_contract=( "Liefert einen gewichteten Gesamtscore (0-100) für Trainingsaktivität. " "Berechnung: Gewichteter Durchschnitt aus 5 Komponenten: " "1. Volumen (training_minutes_week vs. Ziel), " "2. Frequenz (training_frequency_7d vs. Ziel), " "3. Qualität (quality_sessions_pct), " "4. Balance (Ability Balance Scores), " "5. Recovery (rest_day_compliance). " "Gewichtung dynamisch aus user_focus_area_weights." ), business_meaning=( "Meta-Score für Gesamtaktivitäts-Qualität. " "Kombiniert Volumen, Konsistenz, Qualität, Vielfalt und Regeneration. " "Ziel-abhängig: Gewichtung variiert je nach User-Fokus (Kraft vs. Ausdauer vs. Balance)." ), unit="Score (0-100)", format_hint="Ganzzahl", example_output="78", minimum_data_requirements=None, quality_filter_policy=None, confidence_logic=( "Composite Confidence basierend auf Komponenten-Confidence. " "Mindestens 3/5 Komponenten müssen verfügbar sein für Score-Berechnung." ), missing_value_policy=MissingValuePolicy( available=False, value_raw=None, missing_reason="insufficient_data", legacy_display="nicht genug Daten" ), known_limitations=( "COMPOSITE SCORE: Komplexe Abhängigkeiten zu allen Activity-Metriken. " "FOCUS-DEPENDENT: Gewichtung variiert je nach user_focus_area_weights (dynamisch). " "EDGE CASE: Wenn user_focus_area_weights fehlt → default weighting (alle gleich). " "ZEITFENSTER MIXED: Komponenten nutzen unterschiedliche Zeitfenster (7d, 14d, 28d). " "QUESTIONABLE: Vermischt Metriken mit unterschiedlicher Verlässlichkeit " "(z.B. quality_sessions_pct hat TO_VERIFY Issues)." ), layer_1_decision="Data Layer (scores.calculate_activity_score)", layer_2a_decision="Placeholder Resolver (formatting only)", layer_2b_reuse_possible=False, architecture_alignment="Phase 0c Multi-Layer Architecture conform", issue_53_alignment="Layer separation established" ) activity_score_metadata.set_evidence("key", EvidenceType.CODE_DERIVED) activity_score_metadata.set_evidence("category", EvidenceType.CODE_DERIVED) activity_score_metadata.set_evidence("description", EvidenceType.DRAFT_DERIVED) activity_score_metadata.set_evidence("resolver_module", EvidenceType.CODE_DERIVED) activity_score_metadata.set_evidence("resolver_function", EvidenceType.CODE_DERIVED) activity_score_metadata.set_evidence("data_layer_module", EvidenceType.CODE_DERIVED) activity_score_metadata.set_evidence("data_layer_function", EvidenceType.CODE_DERIVED) activity_score_metadata.set_evidence("source_tables", EvidenceType.CODE_DERIVED) activity_score_metadata.set_evidence("time_window", EvidenceType.CODE_DERIVED) activity_score_metadata.set_evidence("output_type", EvidenceType.CODE_DERIVED) activity_score_metadata.set_evidence("placeholder_type", EvidenceType.MIXED) activity_score_metadata.set_evidence("semantic_contract", EvidenceType.CODE_DERIVED) activity_score_metadata.set_evidence("business_meaning", EvidenceType.DRAFT_DERIVED) activity_score_metadata.set_evidence("unit", EvidenceType.CODE_DERIVED) activity_score_metadata.set_evidence("format_hint", EvidenceType.CODE_DERIVED) activity_score_metadata.set_evidence("example_output", EvidenceType.CODE_DERIVED) activity_score_metadata.set_evidence("minimum_data_requirements", EvidenceType.UNRESOLVED) activity_score_metadata.set_evidence("quality_filter_policy", EvidenceType.UNRESOLVED) activity_score_metadata.set_evidence("confidence_logic", EvidenceType.MIXED) activity_score_metadata.set_evidence("missing_value_policy", EvidenceType.CODE_DERIVED) activity_score_metadata.set_evidence("known_limitations", EvidenceType.CODE_DERIVED) activity_score_metadata.set_evidence("layer_1_decision", EvidenceType.CODE_DERIVED) activity_score_metadata.set_evidence("layer_2a_decision", EvidenceType.CODE_DERIVED) activity_score_metadata.set_evidence("layer_2b_reuse_possible", EvidenceType.CODE_DERIVED) activity_score_metadata.set_evidence("architecture_alignment", EvidenceType.CODE_DERIVED) activity_score_metadata.set_evidence("issue_53_alignment", EvidenceType.CODE_DERIVED) register_placeholder(activity_score_metadata) # ============================================================================= # Auto-Registration # ============================================================================= # Register all groups when module is imported register_activity_group_1() register_activity_group_2() register_activity_group_3()