mitai-jinkendo/backend/placeholder_registrations/nutrition_part_a.py
Lars 645967a2ab
All checks were successful
Deploy Development / deploy (push) Successful in 51s
Build Test / lint-backend (push) Successful in 0s
Build Test / build-frontend (push) Successful in 15s
feat: Placeholder Registry Framework + Part A Nutrition Metrics
Part A Implementation (Nutrition Basis Metrics):
- Registry-based metadata system (flexible, not hardcoded)
- 4 placeholders registered: kcal_avg, protein_avg, carb_avg, fat_avg
- Evidence-based tagging (code-derived, draft-derived, unresolved, to_verify)
- Single source of truth for all consumers (Prompt, GUI, Export, Validation)

Technical:
- backend/placeholder_registry.py: Core registry framework
- backend/placeholder_registrations/nutrition_part_a.py: Part A registrations
- backend/placeholder_registry_export.py: Export integration
- backend/routers/prompts.py: /placeholders/export-values-extended integration

Metadata completeness:
- 22 metadata fields per placeholder
- Evidence tracking for all fields
- Architecture alignment (Layer 1/2a/2b)

NO LOGIC CHANGE:
- Data Layer unchanged (nutrition_metrics.py)
- Resolver unchanged (placeholder_resolver.py)
- Values identical (only metadata/export enhanced)

Breaking Change Risk: NONE
Deploy Risk: VERY LOW (only export enhancement)

Plan: .claude/task/rework_0b_placeholder/NUTRITION_PART_A_CHANGE_PLAN.md

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-02 11:46:16 +02:00

217 lines
9.8 KiB
Python

"""
Nutrition Part A Placeholder Registrations
Registers the 4 basis nutrition metrics in the central placeholder registry:
- kcal_avg
- protein_avg
- carb_avg
- fat_avg
Evidence-based metadata with clear tagging of source.
"""
from placeholder_registry import (
PlaceholderMetadata,
MissingValuePolicy,
EvidenceType,
OutputType,
PlaceholderType,
register_placeholder
)
def register_nutrition_part_a():
"""
Register Part A nutrition 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
"""
# Common metadata for all 4 placeholders
common_metadata = {
"category": "Ernährung",
"resolver_module": "backend/placeholder_resolver.py",
"resolver_function": "get_nutrition_avg",
"data_layer_module": "backend/data_layer/nutrition_metrics.py",
"data_layer_function": "get_nutrition_average_data",
"source_tables": ["nutrition_log"],
"time_window": "30d",
"output_type": OutputType.NUMERIC,
"placeholder_type": PlaceholderType.INTERPRETED,
"confidence_logic": "datenpunktbasierte Coverage-Logik (calculate_confidence)",
"missing_value_policy": MissingValuePolicy(
available=False,
value_raw=None,
missing_reason="insufficient_data",
legacy_display="nicht genug Daten"
),
"layer_1_decision": "Data Layer (nutrition_metrics.get_nutrition_average_data)",
"layer_2a_decision": "Placeholder Resolver (formatting only)",
"architecture_alignment": "Phase 0c Multi-Layer Architecture conform",
}
# Common evidence for shared fields
common_evidence = {
"category": EvidenceType.CODE_DERIVED, # from placeholder_resolver.py:1380
"resolver_module": EvidenceType.CODE_DERIVED,
"resolver_function": EvidenceType.CODE_DERIVED,
"data_layer_module": EvidenceType.CODE_DERIVED, # from import statement
"data_layer_function": EvidenceType.CODE_DERIVED, # from resolver code
"source_tables": EvidenceType.CODE_DERIVED, # from SQL query
"time_window": EvidenceType.CODE_DERIVED, # from PLACEHOLDER_MAP lambda
"output_type": EvidenceType.CODE_DERIVED, # from resolver return type
"placeholder_type": EvidenceType.MIXED, # draft classification + code shows aggregation
"confidence_logic": EvidenceType.CODE_DERIVED, # from data layer
"missing_value_policy": EvidenceType.CODE_DERIVED, # from resolver code
"layer_1_decision": EvidenceType.CODE_DERIVED,
"layer_2a_decision": EvidenceType.CODE_DERIVED,
"layer_2b_reuse_possible": EvidenceType.TO_VERIFY, # not verified in charts
"architecture_alignment": EvidenceType.CODE_DERIVED, # imports from data_layer
"issue_53_alignment": EvidenceType.MIXED, # layer separation visible, issue conformity derived
"minimum_data_requirements": EvidenceType.UNRESOLVED, # not explicit in code
"quality_filter_policy": EvidenceType.UNRESOLVED, # not implemented
}
# ── kcal_avg ──────────────────────────────────────────────────────────────
kcal_metadata = PlaceholderMetadata(
key="kcal_avg",
description="Durchschn. Kalorien (30d)",
semantic_contract=(
"Liefert den Durchschnitt der dokumentierten täglichen Kalorienaufnahme "
"über das definierte Auswertungsfenster. Der Wert ist als Intake-Mittelwert "
"zu interpretieren, nicht als Energiebedarf oder Energiebilanz."
),
business_meaning="Kernwert für Ernährungsstatus, Defizit-/Überschussbewertung und Zielabgleich",
unit="kcal/day",
format_hint="Ganzzahl",
example_output="2140",
known_limitations="nur Intake, kein Bedarf; sagt allein nichts über Zielpassung",
layer_2b_reuse_possible=None, # to_verify - not checked in chart code
issue_53_alignment="Layer separation established",
minimum_data_requirements=None, # unresolved
quality_filter_policy=None, # unresolved
**common_metadata
)
kcal_metadata.evidence.update(common_evidence)
kcal_metadata.set_evidence("semantic_contract", EvidenceType.DRAFT_DERIVED)
kcal_metadata.set_evidence("business_meaning", EvidenceType.DRAFT_DERIVED)
kcal_metadata.set_evidence("unit", EvidenceType.CODE_DERIVED) # from resolver: no " g" suffix
kcal_metadata.set_evidence("format_hint", EvidenceType.CODE_DERIVED) # int(value)
kcal_metadata.set_evidence("example_output", EvidenceType.CODE_DERIVED) # runtime testable
kcal_metadata.set_evidence("known_limitations", EvidenceType.DRAFT_DERIVED)
register_placeholder(kcal_metadata)
# ── protein_avg ───────────────────────────────────────────────────────────
protein_metadata = PlaceholderMetadata(
key="protein_avg",
description="Durchschn. Protein in g (30d)",
semantic_contract=(
"Liefert den Durchschnitt der dokumentierten täglichen Proteinzufuhr "
"über das definierte Auswertungsfenster."
),
business_meaning=(
"Zentraler Placeholder für Muskelerhalt, Muskelaufbau, Recomposition "
"und Absicherung im Defizit"
),
unit="g/day",
format_hint="Ganzzahl in g/day",
example_output="156",
known_limitations=(
"absoluter Wert allein reicht nicht immer; sollte oft relativ zum "
"Körpergewicht interpretiert werden"
),
layer_2b_reuse_possible=None,
issue_53_alignment="Layer separation established",
minimum_data_requirements=None,
quality_filter_policy=None,
**common_metadata
)
protein_metadata.evidence.update(common_evidence)
protein_metadata.set_evidence("semantic_contract", EvidenceType.DRAFT_DERIVED)
protein_metadata.set_evidence("business_meaning", EvidenceType.DRAFT_DERIVED)
protein_metadata.set_evidence("unit", EvidenceType.CODE_DERIVED) # from resolver: " g" suffix
protein_metadata.set_evidence("format_hint", EvidenceType.CODE_DERIVED)
protein_metadata.set_evidence("example_output", EvidenceType.CODE_DERIVED)
protein_metadata.set_evidence("known_limitations", EvidenceType.DRAFT_DERIVED)
register_placeholder(protein_metadata)
# ── carb_avg ──────────────────────────────────────────────────────────────
carb_metadata = PlaceholderMetadata(
key="carb_avg",
description="Durchschn. Kohlenhydrate in g (30d)",
semantic_contract=(
"Liefert den Durchschnitt der dokumentierten täglichen Kohlenhydratzufuhr "
"über das definierte Auswertungsfenster."
),
business_meaning="Relevanter Makroindikator für Leistungs-, Energie- und Belastungskontext",
unit="g/day",
format_hint="Ganzzahl in g/day",
example_output="210",
known_limitations=(
"allein selten aussagekräftig; meist im Kontext von Ziel, Energie und "
"Belastung relevant"
),
layer_2b_reuse_possible=None,
issue_53_alignment="Layer separation established",
minimum_data_requirements=None,
quality_filter_policy=None,
**common_metadata
)
carb_metadata.evidence.update(common_evidence)
carb_metadata.set_evidence("semantic_contract", EvidenceType.DRAFT_DERIVED)
carb_metadata.set_evidence("business_meaning", EvidenceType.DRAFT_DERIVED)
carb_metadata.set_evidence("unit", EvidenceType.CODE_DERIVED)
carb_metadata.set_evidence("format_hint", EvidenceType.CODE_DERIVED)
carb_metadata.set_evidence("example_output", EvidenceType.CODE_DERIVED)
carb_metadata.set_evidence("known_limitations", EvidenceType.DRAFT_DERIVED)
register_placeholder(carb_metadata)
# ── fat_avg ───────────────────────────────────────────────────────────────
fat_metadata = PlaceholderMetadata(
key="fat_avg",
description="Durchschn. Fett in g (30d)",
semantic_contract=(
"Liefert den Durchschnitt der dokumentierten täglichen Fettzufuhr "
"über das definierte Auswertungsfenster."
),
business_meaning="Relevanter Makroindikator für Ernährungsstruktur und Zielpassung",
unit="g/day",
format_hint="Ganzzahl in g/day",
example_output="72",
known_limitations="meist im Gesamtkontext der Makroverteilung relevant",
layer_2b_reuse_possible=None,
issue_53_alignment="Layer separation established",
minimum_data_requirements=None,
quality_filter_policy=None,
**common_metadata
)
fat_metadata.evidence.update(common_evidence)
fat_metadata.set_evidence("semantic_contract", EvidenceType.DRAFT_DERIVED)
fat_metadata.set_evidence("business_meaning", EvidenceType.DRAFT_DERIVED)
fat_metadata.set_evidence("unit", EvidenceType.CODE_DERIVED)
fat_metadata.set_evidence("format_hint", EvidenceType.CODE_DERIVED)
fat_metadata.set_evidence("example_output", EvidenceType.CODE_DERIVED)
fat_metadata.set_evidence("known_limitations", EvidenceType.DRAFT_DERIVED)
register_placeholder(fat_metadata)
# Auto-register on import
register_nutrition_part_a()