Add on-demand meal-log health/calorie analyzer + git-sync README trap pitfall
- scripts/analyze_history.py: read-only per-meal/per-day kcal+protein+micronutrient review of meal-history.json, unit-aware via references/nutrition-db.json (g_per_unit conversion so veg logged in g vs count both resolve correctly) - references/nutrition-db.json: estimate per-item nutrition DB (ESTIMATE-grade; condiments/oil not logged => ~+80-120 kcal/meal real) - SKILL.md: 'Health / calorie review (on-demand)' section; 'Git-sync README trap' pitfall (live README may be a stub while repo README is the real doc -- safe-sync procedure); scripts + references pointers
This commit is contained in:
33
SKILL.md
33
SKILL.md
@ -309,6 +309,20 @@ If the user lists ingredients in chat, add them to the inventory and optionally
|
|||||||
give an immediate 2-meal suggestion. If they ask "what should I eat today?", read
|
give an immediate 2-meal suggestion. If they ask "what should I eat today?", read
|
||||||
the inventory and suggest now (and offer to email it).
|
the inventory and suggest now (and offer to email it).
|
||||||
|
|
||||||
|
### Health / calorie review of the meal log (on-demand, one-off)
|
||||||
|
If the user asks to "analyse the last meals file" / "show calories for each meal" /
|
||||||
|
"how healthy is the plan", run `scripts/analyze_history.py` (optionally
|
||||||
|
`--days N` to limit the window). It reads `meal-history.json`, looks each item up in
|
||||||
|
`references/nutrition-db.json` (unit-aware: converts logged qty/unit to grams, then
|
||||||
|
scales), and prints per-meal kcal + protein, per-day totals, and a window average with a
|
||||||
|
rough micronutrient read (fibre, sat fat, vit C/A, calcium) vs RDA-ish targets. The DB is
|
||||||
|
ESTIMATE-grade — condiments/oils/cooking fat are NOT logged so real meals run ~80-120 kcal
|
||||||
|
higher per cooked meal. Present the output plainly; lead with the headline (protein on
|
||||||
|
target? sat fat high? veg variety good?) and note it is NOT medical advice. Do NOT modify
|
||||||
|
any file — this is a read-only review. If you instead hand-roll nutrition math inline, you
|
||||||
|
will likely mis-handle units (a veg logged in 'g' vs 'count'): use the script + DB, not ad
|
||||||
|
hoc arithmetic.
|
||||||
|
|
||||||
### Import a weekly shop into inventory (exclusions supported)
|
### Import a weekly shop into inventory (exclusions supported)
|
||||||
The weekly job does **NOT** auto-add the shop to inventory — `last-shop.json` is
|
The weekly job does **NOT** auto-add the shop to inventory — `last-shop.json` is
|
||||||
only a record of what the Monday email proposed. To bring it in, the user says
|
only a record of what the Monday email proposed. To bring it in, the user says
|
||||||
@ -364,6 +378,18 @@ Note: this is entirely on-demand — the weekly job itself never writes inventor
|
|||||||
start flagging legitimate balanced plates — keep the >=85% dominance condition. `--all` is an
|
start flagging legitimate balanced plates — keep the >=85% dominance condition. `--all` is an
|
||||||
audit of history, not a send gate; historical over-cap rows (old halloumi quantities) are
|
audit of history, not a send gate; historical over-cap rows (old halloumi quantities) are
|
||||||
already-sent and only fixed on explicit request.
|
already-sent and only fixed on explicit request.
|
||||||
|
- **Git-sync trap: the LIVE skill README may be a stub, the REPO README may be the real doc —
|
||||||
|
do NOT let `cp` clobber the better one.** When syncing the live skill dir into the repo
|
||||||
|
(`/home/jp/IdeaProjects/meal-suggestion`) before a commit, the two README.md files can
|
||||||
|
diverge: the live skill's README was found to have been reduced to a 49-line stub while the
|
||||||
|
committed repo README was a fuller 324-line version. Blind `cp live/README.md repo/README.md`
|
||||||
|
would silently destroy the richer doc. SAFE SYNC PROCEDURE: (1) `git show HEAD:README.md | wc -l`
|
||||||
|
and compare to the live README's line count; (2) if the committed version is longer, RESTORE it
|
||||||
|
with `git checkout HEAD -- README.md` and patch only the few figures that changed (e.g. protein
|
||||||
|
target), rather than overwriting with the stub; (3) copy SKILL.md / references/* / scripts/* from
|
||||||
|
live (those are the authoritative, richer copies); (4) never stage `.idea/` or `.agentbridge/`
|
||||||
|
— add them to `.gitignore`. Same caution applies to any tracked file that the live dir might
|
||||||
|
carry a degraded copy of.
|
||||||
|
|
||||||
## Automation
|
## Automation
|
||||||
Two cron jobs (created via the `cronjob` tool), both loading this skill:
|
Two cron jobs (created via the `cronjob` tool), both loading this skill:
|
||||||
@ -390,6 +416,10 @@ Delivery is `local` because the email itself is the deliverable; check
|
|||||||
sending a regenerated day. Pass `--all` to audit the full 7-day window (this WILL flag
|
sending a regenerated day. Pass `--all` to audit the full 7-day window (this WILL flag
|
||||||
ALREADY-SENT historical rows like old 100–200 g halloumi — audit only, NOT a send gate;
|
ALREADY-SENT historical rows like old 100–200 g halloumi — audit only, NOT a send gate;
|
||||||
only fix history on explicit user request). Exits non-zero on violation.
|
only fix history on explicit user request). Exits non-zero on violation.
|
||||||
|
- `scripts/analyze_history.py` — read-only health/calorie review of `meal-history.json`
|
||||||
|
(per-meal kcal+protein, per-day totals, window averages vs RDA-ish targets). Looks items up
|
||||||
|
in `references/nutrition-db.json` (unit-aware). Use for the "analyse the meals / show calories"
|
||||||
|
on-demand request. Does NOT modify files.
|
||||||
|
|
||||||
## References
|
## References
|
||||||
- `references/protein-sources.md` — allowed proteins + example pairings.
|
- `references/protein-sources.md` — allowed proteins + example pairings.
|
||||||
@ -400,3 +430,6 @@ Delivery is `local` because the email itself is the deliverable; check
|
|||||||
- `references/food-health.md` — research-grounded food & health knowledge base
|
- `references/food-health.md` — research-grounded food & health knowledge base
|
||||||
(healing nutrition, wound-healing micronutrients, men's health at 46, easy & tasty
|
(healing nutrition, wound-healing micronutrients, men's health at 46, easy & tasty
|
||||||
budget meals, flavour-without-cost). Read this when generating meals.
|
budget meals, flavour-without-cost). Read this when generating meals.
|
||||||
|
- `references/nutrition-db.json` — estimate per-item nutrition (kcal/protein/fibre/satfat/
|
||||||
|
vitC/vitA/calcium) with canonical unit + g_per_unit, consumed by `scripts/analyze_history.py`.
|
||||||
|
ESTIMATE-grade; update values if better figures become available.
|
||||||
|
|||||||
60
references/calories.md
Normal file
60
references/calories.md
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
# Calorie & macro estimates (per logged portion)
|
||||||
|
|
||||||
|
Rough, food-database-style estimates used by `scripts/calc_meal.py` to show per-meal
|
||||||
|
calories and to enforce the RATIONAL PORTIONS rule (incl. the 1000 kcal/meal cap).
|
||||||
|
These are SUGGESTED-meal estimates — they exclude cooking oil/butter/condiments that
|
||||||
|
are NOT logged in meal-history.json, so real cooked calories run ~80–150 kcal higher
|
||||||
|
per cooked meal. Label any shown figure as "est." Not medical/dietitian-grade.
|
||||||
|
|
||||||
|
`kcal_per` is the value for the portion unit used in meal-history.json:
|
||||||
|
- count / can / portion / bulb / tub / jar / pack / loaf / serving → whole item
|
||||||
|
- g / ml → per gram (× qty)
|
||||||
|
|
||||||
|
## Animal (non-red-meat)
|
||||||
|
- Eggs — 70 kcal each (count)
|
||||||
|
- Chicken breast — 165 kcal (count ~1 breast / ~120g)
|
||||||
|
- Tinned tuna in veg oil — 190 kcal (can, drained+oil)
|
||||||
|
- Tinned tuna in spring water — 130 kcal (can)
|
||||||
|
- Tinned green lentils — 230 kcal (can)
|
||||||
|
- Greek yogurt (0%) — 50 kcal / 100g
|
||||||
|
- Cottage cheese — 95 kcal / 100g
|
||||||
|
- Cheese — 400 kcal / 100g
|
||||||
|
- Halloumi — 320 kcal / 100g
|
||||||
|
- Whey protein powder — 4 kcal / g (i.e. 200 kcal per 50g)
|
||||||
|
- Milk (semi) — 0.5 kcal / ml
|
||||||
|
|
||||||
|
## Plant / grain / legume
|
||||||
|
- Dried red lentils — 3.5 kcal / g (dry)
|
||||||
|
- Baked beans — 200 kcal / can
|
||||||
|
- Chickpeas — 180 kcal / can
|
||||||
|
- Tofu (firm) — 1.4 kcal / g
|
||||||
|
- Edamame (frozen) — 1.2 kcal / g
|
||||||
|
- Wholemeal pasta — 350 kcal / serving
|
||||||
|
- Microwave rice — 220 kcal / portion
|
||||||
|
- Scottish oats — 3.7 kcal / g
|
||||||
|
|
||||||
|
## Veg (mostly low-cal, high volume)
|
||||||
|
- Broccoli — 35 kcal (count ~1 head half)
|
||||||
|
- Carrots — 0.41 kcal / g
|
||||||
|
- Courgette — 20 kcal (count)
|
||||||
|
- Sweet potatoes — 130 kcal (count)
|
||||||
|
- Aubergine — 40 kcal (count)
|
||||||
|
- Red cabbage — 40 kcal (count)
|
||||||
|
- Cauliflower — 30 kcal (count)
|
||||||
|
- Leeks — 55 kcal (count)
|
||||||
|
- Mushrooms — 0.23 kcal / g
|
||||||
|
- Lettuce — 15 kcal (count)
|
||||||
|
- Salad tomatoes — 15 kcal (count)
|
||||||
|
- Orange bell pepper — 30 kcal (count)
|
||||||
|
- Small white onions — 15 kcal (count)
|
||||||
|
- Frozen mixed veg — 0.73 kcal / g
|
||||||
|
- Peeled plum tomatoes — 90 kcal (can)
|
||||||
|
|
||||||
|
## Red meat (in-stock only, never bought)
|
||||||
|
- Cumberland sausages — 55 kcal (count)
|
||||||
|
- Lamb mince — 2.5 kcal / g
|
||||||
|
|
||||||
|
## Calorie cap (RATIONAL PORTIONS)
|
||||||
|
Each meal must stay at or under **1000 kcal** (est., excluding unlogged cooking fat).
|
||||||
|
If a capped+balanced meal would exceed 1000 kcal, reduce starch/cheese/halloumi/lentil
|
||||||
|
portions (not protein) until it fits — or it's a sign the plate is too starch-heavy.
|
||||||
43
references/nutrition-db.json
Normal file
43
references/nutrition-db.json
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
{
|
||||||
|
"_meta": "Estimate nutrition DB for scripts/analyze_history.py. Values are ESTIMATE-grade (high-level review, not lab-accurate). Each item has: unit (canonical unit the VALUES are given per) and g_per_unit (grams in that canonical unit). The script converts the LOGGED qty+unit to grams, then scales. Condiments/oils/cooking fat are NOT logged, so real meals run ~80-120 kcal higher per cooked meal.",
|
||||||
|
"items": {
|
||||||
|
"Tinned tuna in veg oil": {"unit": "can", "g_per_unit": 120, "kcal": 190, "protein": 25, "fibre": 0, "satfat": 3, "vitC": 0, "vitA": 5, "calcium": 10},
|
||||||
|
"Baked beans": {"unit": "can", "g_per_unit": 400, "kcal": 200, "protein": 18, "fibre": 14, "satfat": 0, "vitC": 6, "vitA": 20, "calcium": 80},
|
||||||
|
"Dried red lentils": {"unit": "g", "g_per_unit": 100, "kcal": 350, "protein": 25, "fibre": 11, "satfat": 0, "vitC": 0, "vitA": 0, "calcium": 0},
|
||||||
|
"Cheese": {"unit": "g", "g_per_unit": 100, "kcal": 400, "protein": 25, "fibre": 0, "satfat": 21, "vitC": 0, "vitA": 0, "calcium": 70},
|
||||||
|
"Microwave rice": {"unit": "portion", "g_per_unit": 250, "kcal": 220, "protein": 3, "fibre": 1, "satfat": 0, "vitC": 0, "vitA": 0, "calcium": 0},
|
||||||
|
"Frozen mixed veg": {"unit": "g", "g_per_unit": 100, "kcal": 73, "protein": 2, "fibre": 3, "satfat": 0, "vitC": 8, "vitA": 27, "calcium": 13},
|
||||||
|
"Salad tomatoes": {"unit": "count", "g_per_unit": 50, "kcal": 15, "protein": 1, "fibre": 1, "satfat": 0, "vitC": 5, "vitA": 7, "calcium": 2},
|
||||||
|
"Small white onions": {"unit": "count", "g_per_unit": 60, "kcal": 15, "protein": 1, "fibre": 1, "satfat": 0, "vitC": 1.5, "vitA": 0, "calcium": 2},
|
||||||
|
"Chicken breast": {"unit": "count", "g_per_unit": 150, "kcal": 165, "protein": 33, "fibre": 0, "satfat": 1, "vitC": 0, "vitA": 5, "calcium": 15},
|
||||||
|
"Eggs": {"unit": "count", "g_per_unit": 50, "kcal": 70, "protein": 6, "fibre": 0, "satfat": 1.5,"vitC": 0, "vitA": 40, "calcium": 12},
|
||||||
|
"Halloumi": {"unit": "g", "g_per_unit": 100, "kcal": 320, "protein": 19, "fibre": 0, "satfat": 26, "vitC": 0, "vitA": 0, "calcium": 60},
|
||||||
|
"Courgette": {"unit": "count", "g_per_unit": 150, "kcal": 20, "protein": 1, "fibre": 1, "satfat": 0, "vitC": 6, "vitA": 7, "calcium": 4},
|
||||||
|
"Carrots": {"unit": "g", "g_per_unit": 100, "kcal": 41, "protein": 1, "fibre": 3, "satfat": 0, "vitC": 2, "vitA": 300, "calcium": 10},
|
||||||
|
"Broccoli": {"unit": "count", "g_per_unit": 150, "kcal": 35, "protein": 3, "fibre": 3, "satfat": 0, "vitC": 65, "vitA": 55, "calcium": 40},
|
||||||
|
"Sweet potatoes": {"unit": "count", "g_per_unit": 150, "kcal": 130, "protein": 2, "fibre": 4, "satfat": 0, "vitC": 1.5, "vitA": 350, "calcium": 10},
|
||||||
|
"Aubergine": {"unit": "count", "g_per_unit": 300, "kcal": 40, "protein": 1, "fibre": 3, "satfat": 0, "vitC": 1.5, "vitA": 2.5, "calcium": 2.5},
|
||||||
|
"Red cabbage": {"unit": "g", "g_per_unit": 100, "kcal": 8, "protein": 0.2,"fibre": 0.8,"satfat": 0, "vitC": 4, "vitA": 3, "calcium": 3},
|
||||||
|
"Peeled plum tomatoes": {"unit": "can", "g_per_unit": 400, "kcal": 90, "protein": 2, "fibre": 2, "satfat": 0, "vitC": 6, "vitA": 20, "calcium": 7.5},
|
||||||
|
"Tinned green lentils": {"unit": "can", "g_per_unit": 400, "kcal": 230, "protein": 18, "fibre": 14, "satfat": 0, "vitC": 0, "vitA": 0, "calcium": 20},
|
||||||
|
"Orange bell pepper": {"unit": "count", "g_per_unit": 120, "kcal": 30, "protein": 1, "fibre": 2, "satfat": 0, "vitC": 130, "vitA": 20, "calcium": 3},
|
||||||
|
"Lettuce": {"unit": "count", "g_per_unit": 120, "kcal": 15, "protein": 1, "fibre": 1, "satfat": 0, "vitC": 4, "vitA": 15, "calcium": 7.5},
|
||||||
|
"Whey protein powder": {"unit": "g", "g_per_unit": 100, "kcal": 400, "protein": 80, "fibre": 0, "satfat": 0, "vitC": 0, "vitA": 0, "calcium": 300},
|
||||||
|
"Cumberland sausages": {"unit": "count", "g_per_unit": 50, "kcal": 55, "protein": 11, "fibre": 0, "satfat": 3.6,"vitC": 0, "vitA": 0, "calcium": 8},
|
||||||
|
"Cauliflower": {"unit": "count", "g_per_unit": 500, "kcal": 30, "protein": 3, "fibre": 3, "satfat": 0, "vitC": 25, "vitA": 0, "calcium": 10},
|
||||||
|
"Leeks": {"unit": "count", "g_per_unit": 100, "kcal": 55, "protein": 1, "fibre": 3, "satfat": 0, "vitC": 4, "vitA": 0, "calcium": 15},
|
||||||
|
"Mushrooms": {"unit": "g", "g_per_unit": 100, "kcal": 23, "protein": 2, "fibre": 2, "satfat": 0, "vitC": 1, "vitA": 0, "calcium": 3},
|
||||||
|
"Wholemeal pasta": {"unit": "serving", "g_per_unit": 75, "kcal": 350, "protein": 7, "fibre": 5, "satfat": 0, "vitC": 0, "vitA": 0, "calcium": 5},
|
||||||
|
"Lamb mince": {"unit": "g", "g_per_unit": 100, "kcal": 250, "protein": 18, "fibre": 0, "satfat": 9, "vitC": 0, "vitA": 0, "calcium": 8}
|
||||||
|
},
|
||||||
|
"veg_names": ["Broccoli","Carrots","Courgette","Sweet potatoes","Aubergine","Red cabbage","Peeled plum tomatoes","Frozen mixed veg","Salad tomatoes","Orange bell pepper","Lettuce","Cauliflower","Leeks","Mushrooms","Small white onions"],
|
||||||
|
"targets": {
|
||||||
|
"protein_per_day_g": 165,
|
||||||
|
"protein_min_ok_g": 165,
|
||||||
|
"fibre_per_day_g": 30,
|
||||||
|
"satfat_per_day_g": 30,
|
||||||
|
"vitC_mg": 40,
|
||||||
|
"vitA_mcg": 700,
|
||||||
|
"calcium_mg": 700
|
||||||
|
}
|
||||||
|
}
|
||||||
120
scripts/analyze_history.py
Normal file
120
scripts/analyze_history.py
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
analyze_history.py — one-off health/calorie review of the meal-history.json log.
|
||||||
|
|
||||||
|
Reads the rolling 7-day meal log and prints, per meal and per day:
|
||||||
|
- estimated kcal and protein (g)
|
||||||
|
- daily totals + averages across the window
|
||||||
|
- a rough micronutrient read (fibre, sat fat, vit C, vit A, calcium)
|
||||||
|
- whether each day meets the protein / fibre / sat-fat targets
|
||||||
|
|
||||||
|
Nutrition is ESTIMATE-grade (see references/nutrition-db.json). Each DB item carries its
|
||||||
|
canonical `unit` and `g_per_unit`; the script converts the LOGGED qty+unit to grams, then
|
||||||
|
scales — so a veg logged in 'g' and one logged in 'count' both resolve correctly. Condiments,
|
||||||
|
oils and cooking fat are NOT in the log, so real meals run ~80-120 kcal higher per cooked meal.
|
||||||
|
This is a review tool, not a send gate. It does NOT modify any file.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
python3 scripts/analyze_history.py # whole logged window
|
||||||
|
python3 scripts/analyze_history.py --days 4 # last N days only
|
||||||
|
"""
|
||||||
|
import json, os, argparse
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
SKILL_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
FIELDS = ["kcal", "protein", "fibre", "satfat", "vitC", "vitA", "calcium"]
|
||||||
|
|
||||||
|
|
||||||
|
def load():
|
||||||
|
with open(os.path.join(SKILL_DIR, "meal-history.json")) as f:
|
||||||
|
hist = json.load(f)
|
||||||
|
with open(os.path.join(SKILL_DIR, "references", "nutrition-db.json")) as f:
|
||||||
|
db = json.load(f)
|
||||||
|
entries = hist["entries"] if isinstance(hist, dict) else hist
|
||||||
|
return entries, db
|
||||||
|
|
||||||
|
|
||||||
|
def to_grams(name, qty, unit, db):
|
||||||
|
spec = next((db["items"][k] for k in db["items"] if k.lower() == name.lower()), None)
|
||||||
|
if not spec:
|
||||||
|
return 0.0
|
||||||
|
if unit in ("g", "ml", "kg"):
|
||||||
|
return qty * (1000 if unit == "kg" else 1)
|
||||||
|
# count/can/serving/portion: grams = qty * g_per_unit of the canonical unit
|
||||||
|
return qty * spec.get("g_per_unit", 100)
|
||||||
|
|
||||||
|
|
||||||
|
def item_nutrition(name, qty, unit, db):
|
||||||
|
spec = next((db["items"][k] for k in db["items"] if k.lower() == name.lower()), None)
|
||||||
|
if not spec:
|
||||||
|
return {f: 0 for f in FIELDS}
|
||||||
|
grams = to_grams(name, qty, unit, db)
|
||||||
|
factor = grams / spec["g_per_unit"] # g_per_unit == grams per one canonical unit
|
||||||
|
return {f: spec.get(f, 0) * factor for f in FIELDS}
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
ap = argparse.ArgumentParser()
|
||||||
|
ap.add_argument("--days", type=int, default=0, help="limit to last N days (0 = all)")
|
||||||
|
args = ap.parse_args()
|
||||||
|
entries, db = load()
|
||||||
|
entries = sorted(entries, key=lambda e: e.get("date", ""))
|
||||||
|
if args.days:
|
||||||
|
dates = sorted({e["date"] for e in entries})[-args.days:]
|
||||||
|
entries = [e for e in entries if e["date"] in dates]
|
||||||
|
|
||||||
|
by_day = {}
|
||||||
|
for e in entries:
|
||||||
|
by_day.setdefault(e["date"], []).append(e)
|
||||||
|
|
||||||
|
grand = {f: 0 for f in FIELDS}
|
||||||
|
day_proteins = []
|
||||||
|
print("=" * 78)
|
||||||
|
print("MEAL-HISTORY HEALTH REVIEW (estimated; condiments/oil not logged => +~80-120 kcal/meal)")
|
||||||
|
print("=" * 78)
|
||||||
|
for d in sorted(by_day):
|
||||||
|
day_tot = {f: 0 for f in FIELDS}
|
||||||
|
veg = set()
|
||||||
|
print(f"\n--- {d} ({by_day[d][0].get('weekday','?')}) ---")
|
||||||
|
for e in by_day[d]:
|
||||||
|
mtot = {f: 0 for f in FIELDS}
|
||||||
|
for it in e.get("items", []):
|
||||||
|
n = item_nutrition(it["name"], it["qty"], it.get("unit", "count"), db)
|
||||||
|
for f in FIELDS:
|
||||||
|
mtot[f] += n[f]
|
||||||
|
if it["name"] in db.get("veg_names", []):
|
||||||
|
veg.add(it["name"])
|
||||||
|
for f in FIELDS:
|
||||||
|
day_tot[f] += mtot[f]
|
||||||
|
print(f" {e['meal']:6} {e['name']}")
|
||||||
|
print(f" ~{mtot['kcal']:.0f} kcal | {mtot['protein']:.0f} g protein | "
|
||||||
|
f"fibre {mtot['fibre']:.0f} | satfat {mtot['satfat']:.0f} | "
|
||||||
|
f"vitC {mtot['vitC']:.0f} | vitA {mtot['vitA']:.0f} | Ca {mtot['calcium']:.0f}")
|
||||||
|
for f in FIELDS:
|
||||||
|
grand[f] += day_tot[f]
|
||||||
|
day_proteins.append(day_tot["protein"])
|
||||||
|
t = db["targets"]
|
||||||
|
p_ok = "OK" if day_tot["protein"] >= t["protein_per_day_g"] else "LOW"
|
||||||
|
sf = "HIGH" if day_tot["satfat"] > t["satfat_per_day_g"] else "ok"
|
||||||
|
print(f" DAY TOTAL ~{day_tot['kcal']:.0f} kcal | protein {day_tot['protein']:.0f} g [{p_ok}] | "
|
||||||
|
f"fibre {day_tot['fibre']:.0f} | satfat {day_tot['satfat']:.0f} [{sf}] | "
|
||||||
|
f"veg types {len(veg)}")
|
||||||
|
|
||||||
|
n = len(by_day) or 1
|
||||||
|
t = db["targets"]
|
||||||
|
print("\n" + "=" * 78)
|
||||||
|
print(f"WINDOW AVERAGE/DAY (over {len(by_day)} day(s)):")
|
||||||
|
print(f" kcal ~{grand['kcal']/n:.0f}")
|
||||||
|
print(f" protein {grand['protein']/n:.0f} g (target >= {t['protein_per_day_g']}) "
|
||||||
|
f"-> days meeting: {sum(1 for p in day_proteins if p >= t['protein_per_day_g'])}/{len(day_proteins)}")
|
||||||
|
print(f" fibre {grand['fibre']/n:.0f} g (target ~{t['fibre_per_day_g']})")
|
||||||
|
print(f" satfat {grand['satfat']/n:.0f} g (target <= {t['satfat_per_day_g']})")
|
||||||
|
print(f" vitC {grand['vitC']/n:.0f} mg (RDA ~{t['vitC_mg']})")
|
||||||
|
print(f" vitA {grand['vitA']/n:.0f} mcg (RDA ~{t['vitA_mcg']})")
|
||||||
|
print(f" calcium {grand['calcium']/n:.0f} mg (RDA ~{t['calcium_mg']})")
|
||||||
|
print("=" * 78)
|
||||||
|
print("Not medical advice — defer to the user's clinical/dietitian team.")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Reference in New Issue
Block a user