mitai-jinkendo/backend/placeholder_registrations/nutrition_part_b.py
Lars b00f6ac512
All checks were successful
Deploy Development / deploy (push) Successful in 46s
Build Test / lint-backend (push) Successful in 1s
Build Test / build-frontend (push) Successful in 14s
feat: Placeholder Registry Part B - Protein Placeholders
Registers 5 protein-related placeholders with complete metadata:
- protein_ziel_low: Lower protein target (1.6 g/kg × latest weight)
- protein_ziel_high: Upper protein target (2.2 g/kg × latest weight)
- protein_g_per_kg: Protein intake per kg body weight
- protein_days_in_target: Days in protein range (format: 5/7)
- protein_adequacy_28d: Protein adequacy score (0-100)

All placeholders with evidence-based tagging:
- 22 metadata fields per placeholder
- CODE_DERIVED: Technical fields from source inspection
- DRAFT_DERIVED: Semantic fields from canonical requirements
- UNRESOLVED: Fields requiring clarification
- TO_VERIFY: Assumptions needing verification

Critical issues documented in known_limitations:
- protein_g_per_kg: Weight basis inconsistency (protein 7d avg / weight latest)
- protein_adequacy_28d: Score logic explicitly documented (1.4-1.6-2.2 thresholds)

Registry now contains 9 placeholders total (4 Part A + 5 Part B).

Framework: PLACEHOLDER_REGISTRY_FRAMEWORK.md (verbindlich ab 2026-04-02)
Change Plan: .claude/task/rework_0b_placeholder/NUTRITION_PART_B_CHANGE_PLAN.md

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-02 12:27:58 +02:00

430 lines
21 KiB
Python

"""
Nutrition Part B Placeholder Registrations
Registers the 5 protein-specific metrics in the central placeholder registry:
- protein_ziel_low
- protein_ziel_high
- protein_g_per_kg
- protein_days_in_target
- protein_adequacy_28d
Evidence-based metadata with clear tagging of source.
Includes documentation of open points (weight basis inconsistency, score logic).
"""
from placeholder_registry import (
PlaceholderMetadata,
MissingValuePolicy,
EvidenceType,
OutputType,
PlaceholderType,
register_placeholder
)
def register_nutrition_part_b():
"""
Register Part B protein placeholders.
Metadata sources:
- code-derived: extracted from actual code
- draft-derived: from canonical requirements draft
- mixed: combination of code and draft
- unresolved: not explicitly documented
- to_verify: claimed but not verified
"""
# ── protein_ziel_low ──────────────────────────────────────────────────────
low_metadata = PlaceholderMetadata(
key="protein_ziel_low",
category="Ernährung",
description="Unteres Proteinziel (1.6 g/kg)",
# Technical
resolver_module="backend/placeholder_resolver.py",
resolver_function="get_protein_ziel_low",
data_layer_module="backend/data_layer/nutrition_metrics.py",
data_layer_function="get_protein_targets_data",
source_tables=["weight_log"],
# Semantic
semantic_contract=(
"Liefert die untere Proteinziel-Grenze basierend auf aktuellem "
"Körpergewicht (1.6 g/kg). Ziel für Muskelerhalt in Maintenance-Phasen."
),
business_meaning="Maintenance-Ziel für Muskelerhalt",
unit="g/day",
time_window="snapshot",
output_type=OutputType.NUMERIC,
placeholder_type=PlaceholderType.INTERPRETED,
format_hint="Ganzzahl",
example_output="128",
# Quality
confidence_logic="Binary: weight vorhanden/nicht vorhanden",
missing_value_policy=MissingValuePolicy(
available=False,
value_raw=None,
missing_reason="no_data",
legacy_display="nicht verfügbar"
),
known_limitations=(
"Basiert auf single-point weight (latest entry); "
"anfällig für Gewichts-Outlier (z.B. nach Refeed-Tag)"
),
# Architecture
layer_1_decision="Data Layer (nutrition_metrics.get_protein_targets_data)",
layer_2a_decision="Placeholder Resolver (formatting only)",
layer_2b_reuse_possible=None, # to_verify
architecture_alignment="Phase 0c Multi-Layer Architecture conform",
issue_53_alignment="Layer separation established"
)
# Evidence
low_metadata.set_evidence("key", EvidenceType.CODE_DERIVED)
low_metadata.set_evidence("category", EvidenceType.CODE_DERIVED)
low_metadata.set_evidence("description", EvidenceType.MIXED)
low_metadata.set_evidence("resolver_module", EvidenceType.CODE_DERIVED)
low_metadata.set_evidence("resolver_function", EvidenceType.CODE_DERIVED)
low_metadata.set_evidence("data_layer_module", EvidenceType.CODE_DERIVED)
low_metadata.set_evidence("data_layer_function", EvidenceType.CODE_DERIVED)
low_metadata.set_evidence("source_tables", EvidenceType.CODE_DERIVED)
low_metadata.set_evidence("semantic_contract", EvidenceType.DRAFT_DERIVED)
low_metadata.set_evidence("business_meaning", EvidenceType.DRAFT_DERIVED)
low_metadata.set_evidence("unit", EvidenceType.MIXED) # implicit in code, confirmed by draft
low_metadata.set_evidence("time_window", EvidenceType.CODE_DERIVED)
low_metadata.set_evidence("output_type", EvidenceType.CODE_DERIVED)
low_metadata.set_evidence("placeholder_type", EvidenceType.MIXED)
low_metadata.set_evidence("format_hint", EvidenceType.CODE_DERIVED)
low_metadata.set_evidence("example_output", EvidenceType.CODE_DERIVED)
low_metadata.set_evidence("confidence_logic", EvidenceType.CODE_DERIVED)
low_metadata.set_evidence("missing_value_policy", EvidenceType.CODE_DERIVED)
low_metadata.set_evidence("known_limitations", EvidenceType.MIXED)
low_metadata.set_evidence("layer_1_decision", EvidenceType.CODE_DERIVED)
low_metadata.set_evidence("layer_2a_decision", EvidenceType.CODE_DERIVED)
low_metadata.set_evidence("layer_2b_reuse_possible", EvidenceType.TO_VERIFY)
low_metadata.set_evidence("architecture_alignment", EvidenceType.CODE_DERIVED)
low_metadata.set_evidence("issue_53_alignment", EvidenceType.MIXED)
register_placeholder(low_metadata)
# ── protein_ziel_high ─────────────────────────────────────────────────────
high_metadata = PlaceholderMetadata(
key="protein_ziel_high",
category="Ernährung",
description="Oberes Proteinziel (2.2 g/kg)",
# Technical (same as protein_ziel_low)
resolver_module="backend/placeholder_resolver.py",
resolver_function="get_protein_ziel_high",
data_layer_module="backend/data_layer/nutrition_metrics.py",
data_layer_function="get_protein_targets_data",
source_tables=["weight_log"],
# Semantic
semantic_contract=(
"Liefert die obere Proteinziel-Grenze basierend auf aktuellem "
"Körpergewicht (2.2 g/kg). Ziel für Muskelaufbau in hypertrophen Phasen."
),
business_meaning="Muskelaufbau-Ziel für hypertrophe Phasen",
unit="g/day",
time_window="snapshot",
output_type=OutputType.NUMERIC,
placeholder_type=PlaceholderType.INTERPRETED,
format_hint="Ganzzahl",
example_output="176",
# Quality
confidence_logic="Binary: weight vorhanden/nicht vorhanden",
missing_value_policy=MissingValuePolicy(
available=False,
value_raw=None,
missing_reason="no_data",
legacy_display="nicht verfügbar"
),
known_limitations=(
"Basiert auf single-point weight (latest entry); "
"anfällig für Gewichts-Outlier"
),
# Architecture
layer_1_decision="Data Layer (nutrition_metrics.get_protein_targets_data)",
layer_2a_decision="Placeholder Resolver (formatting only)",
layer_2b_reuse_possible=None,
architecture_alignment="Phase 0c Multi-Layer Architecture conform",
issue_53_alignment="Layer separation established"
)
# Evidence (identical to protein_ziel_low)
high_metadata.set_evidence("key", EvidenceType.CODE_DERIVED)
high_metadata.set_evidence("category", EvidenceType.CODE_DERIVED)
high_metadata.set_evidence("description", EvidenceType.MIXED)
high_metadata.set_evidence("resolver_module", EvidenceType.CODE_DERIVED)
high_metadata.set_evidence("resolver_function", EvidenceType.CODE_DERIVED)
high_metadata.set_evidence("data_layer_module", EvidenceType.CODE_DERIVED)
high_metadata.set_evidence("data_layer_function", EvidenceType.CODE_DERIVED)
high_metadata.set_evidence("source_tables", EvidenceType.CODE_DERIVED)
high_metadata.set_evidence("semantic_contract", EvidenceType.DRAFT_DERIVED)
high_metadata.set_evidence("business_meaning", EvidenceType.DRAFT_DERIVED)
high_metadata.set_evidence("unit", EvidenceType.MIXED)
high_metadata.set_evidence("time_window", EvidenceType.CODE_DERIVED)
high_metadata.set_evidence("output_type", EvidenceType.CODE_DERIVED)
high_metadata.set_evidence("placeholder_type", EvidenceType.MIXED)
high_metadata.set_evidence("format_hint", EvidenceType.CODE_DERIVED)
high_metadata.set_evidence("example_output", EvidenceType.CODE_DERIVED)
high_metadata.set_evidence("confidence_logic", EvidenceType.CODE_DERIVED)
high_metadata.set_evidence("missing_value_policy", EvidenceType.CODE_DERIVED)
high_metadata.set_evidence("known_limitations", EvidenceType.MIXED)
high_metadata.set_evidence("layer_1_decision", EvidenceType.CODE_DERIVED)
high_metadata.set_evidence("layer_2a_decision", EvidenceType.CODE_DERIVED)
high_metadata.set_evidence("layer_2b_reuse_possible", EvidenceType.TO_VERIFY)
high_metadata.set_evidence("architecture_alignment", EvidenceType.CODE_DERIVED)
high_metadata.set_evidence("issue_53_alignment", EvidenceType.MIXED)
register_placeholder(high_metadata)
# ── protein_g_per_kg ──────────────────────────────────────────────────────
gpk_metadata = PlaceholderMetadata(
key="protein_g_per_kg",
category="Ernährung",
description="Protein g/kg Körpergewicht",
# Technical
resolver_module="backend/placeholder_resolver.py",
resolver_function="_safe_float",
data_layer_module="backend/data_layer/nutrition_metrics.py",
data_layer_function="calculate_protein_g_per_kg",
source_tables=["nutrition_log", "weight_log"],
# Semantic
semantic_contract=(
"Liefert die durchschnittliche Proteinzufuhr relativ zum Körpergewicht. "
"Berechnung: protein_7d_avg / latest_weight. "
"WICHTIG: Protein ist geglättet (7d), Gewicht ist single-point."
),
business_meaning="Zentraler Zielindikator für Muskelerhalt und Aufbau",
unit="g/kg/day",
time_window="mixed", # protein 7d, weight snapshot
output_type=OutputType.NUMERIC,
placeholder_type=PlaceholderType.INTERPRETED,
format_hint="Dezimalzahl (1-2 Stellen)",
example_output="1.95",
# Quality
confidence_logic="Minimum von protein_confidence und weight_availability",
missing_value_policy=MissingValuePolicy(
available=False,
value_raw=None,
missing_reason="insufficient_data",
legacy_display="nicht verfügbar"
),
known_limitations=(
"KRITISCHE INKONSISTENZ: Protein ist geglättet (7d average), "
"Gewicht ist single-point (latest). Anfällig für Gewichts-Outlier. "
"Ein Refeed-Tag kann den Wert stark verfälschen, obwohl Protein-Intake stabil ist."
),
# Architecture
layer_1_decision="Data Layer (nutrition_metrics.calculate_protein_g_per_kg)",
layer_2a_decision="Placeholder Resolver (_safe_float wrapper)",
layer_2b_reuse_possible=None,
architecture_alignment="Phase 0c Multi-Layer Architecture conform",
issue_53_alignment="Layer separation established"
)
# Evidence
gpk_metadata.set_evidence("key", EvidenceType.CODE_DERIVED)
gpk_metadata.set_evidence("category", EvidenceType.CODE_DERIVED)
gpk_metadata.set_evidence("description", EvidenceType.CODE_DERIVED)
gpk_metadata.set_evidence("resolver_module", EvidenceType.CODE_DERIVED)
gpk_metadata.set_evidence("resolver_function", EvidenceType.CODE_DERIVED)
gpk_metadata.set_evidence("data_layer_module", EvidenceType.CODE_DERIVED)
gpk_metadata.set_evidence("data_layer_function", EvidenceType.CODE_DERIVED)
gpk_metadata.set_evidence("source_tables", EvidenceType.CODE_DERIVED)
gpk_metadata.set_evidence("semantic_contract", EvidenceType.MIXED) # code + explicit documentation
gpk_metadata.set_evidence("business_meaning", EvidenceType.DRAFT_DERIVED)
gpk_metadata.set_evidence("unit", EvidenceType.CODE_DERIVED)
gpk_metadata.set_evidence("time_window", EvidenceType.CODE_DERIVED) # explicitly documented as mixed
gpk_metadata.set_evidence("output_type", EvidenceType.CODE_DERIVED)
gpk_metadata.set_evidence("placeholder_type", EvidenceType.MIXED)
gpk_metadata.set_evidence("format_hint", EvidenceType.CODE_DERIVED)
gpk_metadata.set_evidence("example_output", EvidenceType.CODE_DERIVED)
gpk_metadata.set_evidence("confidence_logic", EvidenceType.UNRESOLVED) # not explicitly documented
gpk_metadata.set_evidence("missing_value_policy", EvidenceType.CODE_DERIVED)
gpk_metadata.set_evidence("known_limitations", EvidenceType.CODE_DERIVED) # identified from code analysis
gpk_metadata.set_evidence("layer_1_decision", EvidenceType.CODE_DERIVED)
gpk_metadata.set_evidence("layer_2a_decision", EvidenceType.CODE_DERIVED)
gpk_metadata.set_evidence("layer_2b_reuse_possible", EvidenceType.TO_VERIFY)
gpk_metadata.set_evidence("architecture_alignment", EvidenceType.CODE_DERIVED)
gpk_metadata.set_evidence("issue_53_alignment", EvidenceType.MIXED)
register_placeholder(gpk_metadata)
# ── protein_days_in_target ────────────────────────────────────────────────
days_metadata = PlaceholderMetadata(
key="protein_days_in_target",
category="Ernährung",
description="Tage im Protein-Zielbereich (7d)",
# Technical
resolver_module="backend/placeholder_resolver.py",
resolver_function="_safe_str",
data_layer_module="backend/data_layer/nutrition_metrics.py",
data_layer_function="calculate_protein_days_in_target",
source_tables=["nutrition_log", "weight_log"],
# Semantic
semantic_contract=(
"Liefert Anzahl Tage im Protein-Zielbereich relativ zu Gesamttagen. "
"Target-Range: 1.6-2.2 g/kg (hardcoded). "
"Format: 'X/Y' (z.B. '5/7' = 5 von 7 Tagen im Ziel)."
),
business_meaning="Adhärenz-Indikator für Proteinversorgung",
unit="days_ratio",
time_window="7d",
output_type=OutputType.STRING,
placeholder_type=PlaceholderType.INTERPRETED,
format_hint="String format 'X/Y' (e.g. '5/7')",
example_output="5/7",
# Quality
confidence_logic="Abhängig von nutrition_log Datenabdeckung",
missing_value_policy=MissingValuePolicy(
available=False,
value_raw=None,
missing_reason="no_data",
legacy_display="nicht verfügbar"
),
known_limitations=(
"Target-Range 1.6-2.2 g/kg fest kodiert (default parameters), "
"nicht konfigurierbar. Keine Integration mit Goal-System."
),
# Architecture
layer_1_decision="Data Layer (nutrition_metrics.calculate_protein_days_in_target)",
layer_2a_decision="Placeholder Resolver (_safe_str wrapper)",
layer_2b_reuse_possible=None,
architecture_alignment="Phase 0c Multi-Layer Architecture conform",
issue_53_alignment="Layer separation established"
)
# Evidence
days_metadata.set_evidence("key", EvidenceType.CODE_DERIVED)
days_metadata.set_evidence("category", EvidenceType.CODE_DERIVED)
days_metadata.set_evidence("description", EvidenceType.MIXED)
days_metadata.set_evidence("resolver_module", EvidenceType.CODE_DERIVED)
days_metadata.set_evidence("resolver_function", EvidenceType.CODE_DERIVED)
days_metadata.set_evidence("data_layer_module", EvidenceType.CODE_DERIVED)
days_metadata.set_evidence("data_layer_function", EvidenceType.CODE_DERIVED)
days_metadata.set_evidence("source_tables", EvidenceType.CODE_DERIVED)
days_metadata.set_evidence("semantic_contract", EvidenceType.MIXED)
days_metadata.set_evidence("business_meaning", EvidenceType.DRAFT_DERIVED)
days_metadata.set_evidence("unit", EvidenceType.CODE_DERIVED)
days_metadata.set_evidence("time_window", EvidenceType.CODE_DERIVED)
days_metadata.set_evidence("output_type", EvidenceType.CODE_DERIVED)
days_metadata.set_evidence("placeholder_type", EvidenceType.MIXED)
days_metadata.set_evidence("format_hint", EvidenceType.CODE_DERIVED)
days_metadata.set_evidence("example_output", EvidenceType.CODE_DERIVED)
days_metadata.set_evidence("confidence_logic", EvidenceType.UNRESOLVED)
days_metadata.set_evidence("missing_value_policy", EvidenceType.CODE_DERIVED)
days_metadata.set_evidence("known_limitations", EvidenceType.CODE_DERIVED)
days_metadata.set_evidence("layer_1_decision", EvidenceType.CODE_DERIVED)
days_metadata.set_evidence("layer_2a_decision", EvidenceType.CODE_DERIVED)
days_metadata.set_evidence("layer_2b_reuse_possible", EvidenceType.TO_VERIFY)
days_metadata.set_evidence("architecture_alignment", EvidenceType.CODE_DERIVED)
days_metadata.set_evidence("issue_53_alignment", EvidenceType.MIXED)
register_placeholder(days_metadata)
# ── protein_adequacy_28d ──────────────────────────────────────────────────
adequacy_metadata = PlaceholderMetadata(
key="protein_adequacy_28d",
category="Ernährung",
description="Protein Adequacy Score (0-100)",
# Technical
resolver_module="backend/placeholder_resolver.py",
resolver_function="_safe_int",
data_layer_module="backend/data_layer/nutrition_metrics.py",
data_layer_function="calculate_protein_adequacy_28d",
source_tables=["nutrition_log", "weight_log"],
# Semantic
semantic_contract=(
"Liefert standardisierten Angemessenheitswert der Proteinversorgung "
"über 28 Tage relativ zu definierten Protein-Zielbereichen (1.6-2.2 g/kg). "
"Score-Logik: "
"- Days in target [1.6-2.2]: 100 points; "
"- Days slightly below [1.4-1.6]: partial points (linear interpolation); "
"- Days far below (<1.4): 0 points; "
"- Days above (>2.2): 100 points (no penalty). "
"Final score: average over 28d."
),
business_meaning="Verdichteter Zielerreichungsindikator für Proteinversorgung",
unit="score",
time_window="28d",
output_type=OutputType.NUMERIC,
placeholder_type=PlaceholderType.SCORE,
format_hint="Integer 0-100, höher = besser",
example_output="82",
# Quality
confidence_logic="Abgeleitet aus Datenabdeckung über 28d",
missing_value_policy=MissingValuePolicy(
available=False,
value_raw=None,
missing_reason="insufficient_data",
legacy_display="nicht verfügbar"
),
known_limitations=(
"Score muss transparent erklärt werden; ohne Skalen-Dokumentation "
"interpretationsanfällig. Scoring-Schwellen [1.4, 1.6, 2.2] nicht explizit "
"im Code dokumentiert, nur in Logik implementiert."
),
# Architecture
layer_1_decision="Data Layer (nutrition_metrics.calculate_protein_adequacy_28d)",
layer_2a_decision="Placeholder Resolver (_safe_int wrapper)",
layer_2b_reuse_possible=None,
architecture_alignment="Phase 0c Multi-Layer Architecture conform",
issue_53_alignment="Layer separation established"
)
# Evidence
adequacy_metadata.set_evidence("key", EvidenceType.CODE_DERIVED)
adequacy_metadata.set_evidence("category", EvidenceType.CODE_DERIVED)
adequacy_metadata.set_evidence("description", EvidenceType.CODE_DERIVED)
adequacy_metadata.set_evidence("resolver_module", EvidenceType.CODE_DERIVED)
adequacy_metadata.set_evidence("resolver_function", EvidenceType.CODE_DERIVED)
adequacy_metadata.set_evidence("data_layer_module", EvidenceType.CODE_DERIVED)
adequacy_metadata.set_evidence("data_layer_function", EvidenceType.CODE_DERIVED)
adequacy_metadata.set_evidence("source_tables", EvidenceType.CODE_DERIVED)
adequacy_metadata.set_evidence("semantic_contract", EvidenceType.MIXED) # code + explicit documentation
adequacy_metadata.set_evidence("business_meaning", EvidenceType.DRAFT_DERIVED)
adequacy_metadata.set_evidence("unit", EvidenceType.CODE_DERIVED)
adequacy_metadata.set_evidence("time_window", EvidenceType.CODE_DERIVED)
adequacy_metadata.set_evidence("output_type", EvidenceType.CODE_DERIVED)
adequacy_metadata.set_evidence("placeholder_type", EvidenceType.MIXED)
adequacy_metadata.set_evidence("format_hint", EvidenceType.CODE_DERIVED)
adequacy_metadata.set_evidence("example_output", EvidenceType.CODE_DERIVED)
adequacy_metadata.set_evidence("confidence_logic", EvidenceType.UNRESOLVED)
adequacy_metadata.set_evidence("missing_value_policy", EvidenceType.CODE_DERIVED)
adequacy_metadata.set_evidence("known_limitations", EvidenceType.MIXED) # code analysis + draft
adequacy_metadata.set_evidence("layer_1_decision", EvidenceType.CODE_DERIVED)
adequacy_metadata.set_evidence("layer_2a_decision", EvidenceType.CODE_DERIVED)
adequacy_metadata.set_evidence("layer_2b_reuse_possible", EvidenceType.TO_VERIFY)
adequacy_metadata.set_evidence("architecture_alignment", EvidenceType.CODE_DERIVED)
adequacy_metadata.set_evidence("issue_53_alignment", EvidenceType.MIXED)
register_placeholder(adequacy_metadata)
# Auto-register on import
register_nutrition_part_b()