mirror of
https://github.com/kuhyx/testsAndMisc.git
synced 2026-07-04 16:03:03 +02:00
Add the diet_guard package: a screen-locking meal-logging gate that fires on 4-hour slots (08/12/16/20) and records calories/macros, persisting an autocompleting food bank. - Trigger fix: the systemd timer fires at session start (Persistent=true) before lightdm has written ~/.Xauthority, so the gate crashed with a TclError instead of locking the screen. Add wait_for_display() / _display_is_ready() in _gatelock.py and wire it into _cli._cmd_gate so the gate retries on the next tick instead of crashing; add Environment=XAUTHORITY=%h/.Xauthority to the service as belt-and-suspenders. - Food-bank hardening: a transiently corrupt food_bank.json was warned about on every keystroke and then silently overwritten (data loss). _read_bank now quarantines it via _quarantine_corrupt_bank() (warn-once + timestamped backup) before starting fresh. - Multi-item meals: new _meal.py (MealItem, meal_total, MEAL_SOURCE), remember_meal() + _upsert() in _foodbank.py, and a "+ Add item" control in the gate that logs both the individual items and the composite meal. - Bundle resolve_nutrition's manual macros into a ManualMacros dataclass to stay within the argument-count limit. diet_guard at 100% branch coverage; full pre-commit suite passes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
66 lines
2.3 KiB
Python
66 lines
2.3 KiB
Python
"""Composite "meal" support for diet_guard.
|
|
|
|
A meal is a named group of individually-macroed items -- e.g. a dinner of
|
|
salad + chicken + rice, each entered with its own calories and macros. The
|
|
meal's nutrition is the sum of its items. Both the individual items and the
|
|
composite meal are saved to the food bank (see
|
|
:func:`python_pkg.diet_guard._foodbank.remember_meal`), so next time each item
|
|
autocompletes on its own and the whole meal can be picked as one summed entry.
|
|
|
|
This module is deliberately pure (no I/O): the sum is a total function of its
|
|
items, which keeps the arithmetic exhaustively unit-testable apart from the
|
|
bank persistence and the gate UI that compose it.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass
|
|
from typing import TYPE_CHECKING
|
|
|
|
from python_pkg.diet_guard._estimator import Nutrition
|
|
|
|
if TYPE_CHECKING:
|
|
from collections.abc import Sequence
|
|
|
|
# Provenance stamped on a summed meal so the log/UI can tell a composite apart
|
|
# from a single looked-up food.
|
|
MEAL_SOURCE = "meal"
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class MealItem:
|
|
"""One named component of a composite meal, with its own nutrition.
|
|
|
|
Attributes:
|
|
name: The component's food name (e.g. ``"chicken"``).
|
|
nutrition: The component's resolved macros for the amount eaten.
|
|
"""
|
|
|
|
name: str
|
|
nutrition: Nutrition
|
|
|
|
|
|
def meal_total(items: Sequence[MealItem]) -> Nutrition:
|
|
"""Return the summed nutrition of a meal's items.
|
|
|
|
Every macro and the portion weight are added across the items and rounded to
|
|
0.1, and the result is stamped ``source=MEAL_SOURCE`` so it is
|
|
distinguishable from a single food. An empty sequence sums to an all-zero
|
|
meal rather than raising, so callers need not special-case "no items yet".
|
|
|
|
Args:
|
|
items: The meal's components.
|
|
|
|
Returns:
|
|
A :class:`~python_pkg.diet_guard._estimator.Nutrition` whose fields are
|
|
the per-item sums.
|
|
"""
|
|
return Nutrition(
|
|
kcal=round(sum((item.nutrition.kcal for item in items), 0.0), 1),
|
|
protein_g=round(sum((item.nutrition.protein_g for item in items), 0.0), 1),
|
|
carbs_g=round(sum((item.nutrition.carbs_g for item in items), 0.0), 1),
|
|
fat_g=round(sum((item.nutrition.fat_g for item in items), 0.0), 1),
|
|
grams=round(sum((item.nutrition.grams for item in items), 0.0), 1),
|
|
source=MEAL_SOURCE,
|
|
)
|