mitai-jinkendo/backend/placeholder_registrations/nutrition_part_a.py
Lars 052ba195cc
All checks were successful
Deploy Development / deploy (push) Successful in 54s
Build Test / pytest-backend (push) Successful in 4s
Build Test / lint-backend (push) Successful in 1s
Build Test / build-frontend (push) Successful in 15s
feat: Update placeholder metadata and nutrition metrics
- Adjusted the total number of placeholders from 116 to 114 across various documentation and code files to reflect the current state of the system.
- Enhanced TDEE calculation logic in `nutrition_metrics.py` to prioritize Mifflin–St Jeor BMR with PAL when demographic data is available, with a fallback to a weight-based estimate.
- Updated placeholder registrations to ensure consistency with the new metadata structure and improved data handling.
- Revised documentation to clarify the authoritative source of placeholder metadata and the implications of the changes on existing functionalities.

These updates improve the accuracy and consistency of the placeholder system and enhance the nutritional assessment capabilities within the application.
2026-04-11 21:11:05 +02:00

216 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",
"minimum_data_requirements": (
"Mind. ein Kalendertag mit nutrition_log im Fenster; Mittelwerte aus täglicher Aggregation. "
"Confidence über calculate_confidence(day_count, days) in get_nutrition_average_data."
),
"quality_filter_policy": (
"Kein Outlier-Filter auf Tagesaggregaten; leere Tage fehlen in der Aggregation (kein Imputing)."
),
}
# 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.CODE_DERIVED,
"quality_filter_policy": EvidenceType.CODE_DERIVED,
}
# ── 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",
**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",
**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",
**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",
**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()