diet-guard/diet_guard/_cli_args.py
Krzysztof kuhy Rudnicki c310861855 slots: minute-of-day consumers (phase 2 of 3: Python side)
Every Python consumer now speaks slot minutes:

- satisfaction is nearest-slot snapping (_slot_wire.satisfied_by_entries)
  at every "is this slot logged?" site: gate, CLI status, MCP, catering
  occupancy and dedup. Nothing on disk is rewritten; 07:00/10:00 entries
  keep covering 07:15/10:15 after a schedule move.
- log_meal writes slot_fields(minute) before signing (slot + slot_min).
- schedule store and sync encode via schedule_to_wire/from_wire; a
  whole-hour schedule stays byte-identical (f/l/n only).
- CLI ate --hour takes HH or HH:MM; MCP log_meal takes "HH:MM" or a
  legacy int hour, validated against today's slots; get_slots returns
  {minute, label}.
- gate History tab: first/last are HH:MM ttk.Comboboxes (quarter-hour
  list, typed times, Up/Down step without posting the popdown), locked
  as disabled; validation mirrors normalized().
- gate timer fires every 15 min; docs rewritten for minutes.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015npsYVB2PdtWkG7MPAi4tn
2026-10-10 19:53:39 +02:00

119 lines
3.8 KiB
Python

"""Argument parsing for the diet_guard CLI.
Split out of :mod:`diet_guard._cli` to hold the repo's 250-line cap. This is
the declarative half -- every subcommand and flag the CLI accepts -- kept apart
from the handlers that act on them, so adding a flag never grows a file that
also contains logic.
Subparsers owned by a sibling handler register themselves through their own
module (``register_averages_subparser``, ``register_sync_subparser``) rather
than being spelled out here, so a subcommand's flags live next to its code.
"""
from __future__ import annotations
import argparse
from diet_guard._cli_averages import register_averages_subparser
from diet_guard._cli_body import register_body_subparsers
from diet_guard._cli_kuchnia import register_kuchnia_subparser
from diet_guard._cli_prune import register_prune_subparser
from diet_guard._cli_sync import register_sync_subparser
def parse_args(argv: list[str]) -> argparse.Namespace:
"""Parse diet_guard CLI arguments."""
parser = argparse.ArgumentParser(
prog="diet_guard",
description="Log calories and check your daily budget.",
)
sub = parser.add_subparsers(dest="command", required=True)
sub.add_parser(
"init",
help="Compute your starting daily budget from biometrics.",
)
ate = sub.add_parser("ate", help="Log a meal you just ate.")
ate.add_argument("description", help='What you ate, e.g. "big mac".')
ate.add_argument(
"--grams",
type=float,
default=None,
help="Portion size in grams (default: OFF serving size, else 100 g).",
)
ate.add_argument(
"--kcal",
type=float,
default=None,
help="Calories entered manually; skips the food bank and OFF lookup.",
)
ate.add_argument(
"--protein",
type=float,
default=None,
help="Protein in grams (recorded with --kcal to seed the food bank).",
)
ate.add_argument(
"--carbs",
type=float,
default=None,
help="Carbohydrate in grams (recorded with --kcal).",
)
ate.add_argument(
"--fat",
type=float,
default=None,
help="Fat in grams (recorded with --kcal).",
)
ate.add_argument(
"--per",
type=float,
default=None,
help="Grams the macros are stated for (e.g. 100 for a per-100 g label);"
" the typed macros are scaled from this to how much you ate.",
)
ate.add_argument(
"--count",
type=float,
default=None,
help="Number of items eaten (e.g. 5 apples) instead of --grams;"
" multiplied by the staple's unit weight.",
)
ate.add_argument(
"--date",
default=None,
help="Pre-log for a future date (YYYY-MM-DD) instead of now; requires --hour.",
)
ate.add_argument(
"--hour",
type=str,
default=None,
help="Meal slot on --date this meal satisfies, as HH:MM or HH (e.g."
" 07:15 or 20); must be one of that day's slots. Requires --date.",
)
sub.add_parser("status", help="Show today's calories and budget band.")
sub.add_parser("undo", help="Remove today's most recent entry.")
register_averages_subparser(sub)
register_kuchnia_subparser(sub)
register_prune_subparser(sub)
register_sync_subparser(sub)
register_body_subparsers(sub)
gate = sub.add_parser(
"gate",
help="Log-to-unlock screen gate (intended to be run by a timer).",
)
gate.add_argument(
"--check",
action="store_true",
help="Headless: exit 0 if NOT due, 1 if a lock is due. Prints, no window.",
)
gate.add_argument(
"--demo",
action="store_true",
help="Show the lock in safe demo mode (local grab + close button).",
)
return parser.parse_args(argv)