diet-guard/diet_guard/_gatelock_body_input.py
Krzysztof kuhy Rudnicki e761926df4
Some checks failed
dependency freshness / dependencies on newest stable (push) Has been cancelled
file-length / file length <= 250 lines (push) Has been cancelled
md-naming / markdown naming convention (push) Has been cancelled
pre-commit / pre-commit (${{ matrix.group }}) (mypy, mypy, ) (push) Has been cancelled
pre-commit / pre-commit (${{ matrix.group }}) (pylint, pylint, ) (push) Has been cancelled
pre-commit / pre-commit (${{ matrix.group }}) (rest, , mypy,pylint) (push) Has been cancelled
Tests / test (push) Has been cancelled
release-apk / release (APK) (push) Has been cancelled
Body tab: weight/body-fat logs, BMI, ideal weight, goal-based kcal target, workouts + Health Connect steps, week planner
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-28 09:03:03 +02:00

63 lines
2.1 KiB
Python

"""Parse and seed the Body tab's inputs -- pure over the tab's variables.
Split from :mod:`diet_guard._gatelock_body` for the 250-line cap. No widget
is built here; the functions only read and write ``StringVar``-likes.
"""
from __future__ import annotations
from datetime import date
from typing import TYPE_CHECKING
from diet_guard._body_energy import ACTIVITY_CHOICES, activity_label
from diet_guard._body_prefs import goal_from_json
if TYPE_CHECKING:
from diet_guard._body_energy import Goal
from diet_guard._gatelock_body_vars import GoalVars
__all__ = ["HEIGHT_CM", "goal_choice", "load_goal", "parse_day", "parse_float"]
# Outside this range (cm) a height is a typo rather than a person.
HEIGHT_CM = (50.0, 260.0)
def parse_float(text: str) -> float | None:
"""``text`` as a float (comma decimals accepted), or None."""
try:
return float(text.strip().replace(",", "."))
except ValueError:
return None
def parse_day(text: str) -> str | None:
"""``text`` as a normalised ``YYYY-MM-DD``, or None."""
try:
return date.fromisoformat(text.strip()).isoformat()
except ValueError:
return None
def load_goal(goal_vars: GoalVars, goal: Goal) -> None:
"""Put ``goal`` into the sentence's pickers."""
goal_vars.direction.set(goal.direction)
goal_vars.amount.set(f"{goal.amount:g}")
goal_vars.weight_unit.set(goal.weight_unit)
goal_vars.time_unit.set(goal.time_unit)
goal_vars.activity.set(activity_label(goal.activity))
def goal_choice(goal_vars: GoalVars, fallback: Goal) -> Goal:
"""The goal the pickers show; an unreadable amount keeps ``fallback``'s."""
labels = {activity_label(key): key for key in ACTIVITY_CHOICES}
amount = parse_float(goal_vars.amount.get())
return goal_from_json(
{
"dir": goal_vars.direction.get(),
"amt": fallback.amount if amount is None or amount < 0 else amount,
"wu": goal_vars.weight_unit.get(),
"tu": goal_vars.time_unit.get(),
"act": labels.get(goal_vars.activity.get(), fallback.activity),
}
)