Coverage for health / cli / manual.py: 0%
102 statements
« prev ^ index » next coverage.py v7.13.4, created at 2026-03-02 17:44 +0800
« prev ^ index » next coverage.py v7.13.4, created at 2026-03-02 17:44 +0800
1"""
2CLI tool for manual health data entry.
3"""
5import sys
6from datetime import date
7from typing import Optional
9import click
11from health.services.manual_log_storage import ManualLogStorage
12from health.models.manual_log import (
13 DietEntry,
14 AlcoholEntry,
15 SupplementEntry,
16 BodyFeelingEntry,
17)
18from health.utils.date_utils import parse_date
19from health.utils.logging_config import setup_logger
21logger = setup_logger(__name__)
24@click.group()
25def cli() -> None:
26 """Manual Health Data Entry CLI."""
27 pass
30def _get_date(date_str: Optional[str]) -> date:
31 """Parse date string or return today."""
32 if date_str:
33 return parse_date(date_str)
34 return date.today()
37@cli.command()
38@click.option("--time", required=True, help="Time of meal (HH:MM)")
39@click.option("--desc", required=True, help="Description of what was eaten")
40@click.option("--type", "meal_type", help="Meal type (breakfast/lunch/dinner/snack)")
41@click.option("--cals", type=int, help="Estimated calories")
42@click.option("--carbs", help="Carb content (Low/Medium/High)")
43@click.option("--notes", help="Additional notes")
44@click.option("--date", "date_str", help="Date of entry (YYYY-MM-DD)")
45def diet(
46 time: str,
47 desc: str,
48 meal_type: Optional[str],
49 cals: Optional[int],
50 carbs: Optional[str],
51 notes: Optional[str],
52 date_str: Optional[str],
53) -> None:
54 """Add a diet entry."""
55 try:
56 storage = ManualLogStorage()
57 target_date = _get_date(date_str)
59 entry = DietEntry(
60 time=time,
61 description=desc,
62 meal_type=meal_type,
63 estimated_calories=cals,
64 carbs=carbs,
65 notes=notes,
66 )
68 storage.add_diet_entry(target_date, entry)
69 click.echo(f"✅ Added diet entry for {target_date}: {desc}")
71 except Exception as e:
72 click.echo(f"❌ Failed to add diet entry: {e}", err=True)
73 sys.exit(1)
76@cli.command()
77@click.option("--time", required=True, help="Time of consumption (HH:MM)")
78@click.option("--type", "drink_type", required=True, help="Type of alcohol")
79@click.option("--amount", required=True, help="Amount consumed")
80@click.option("--food", help="Food eaten with alcohol")
81@click.option("--nac", is_flag=True, help="NAC taken?")
82@click.option("--notes", help="Additional notes")
83@click.option("--date", "date_str", help="Date of entry (YYYY-MM-DD)")
84def alcohol(
85 time: str,
86 drink_type: str,
87 amount: str,
88 food: Optional[str],
89 nac: bool,
90 notes: Optional[str],
91 date_str: Optional[str],
92) -> None:
93 """Add an alcohol entry."""
94 try:
95 storage = ManualLogStorage()
96 target_date = _get_date(date_str)
98 entry = AlcoholEntry(
99 time=time,
100 drink_type=drink_type,
101 amount=amount,
102 food_with_alcohol=food,
103 nac_taken=nac,
104 notes=notes,
105 )
107 storage.add_alcohol_entry(target_date, entry)
108 click.echo(f"✅ Added alcohol entry for {target_date}: {drink_type}")
110 except Exception as e:
111 click.echo(f"❌ Failed to add alcohol entry: {e}", err=True)
112 sys.exit(1)
115@cli.command()
116@click.option("--time", required=True, help="Time taken (HH:MM)")
117@click.option("--name", required=True, help="Supplement name")
118@click.option("--dosage", help="Dosage")
119@click.option("--timing", help="Timing (morning/evening used for sorting)")
120@click.option("--notes", help="Additional notes")
121@click.option("--date", "date_str", help="Date of entry (YYYY-MM-DD)")
122def supp(
123 time: str,
124 name: str,
125 dosage: Optional[str],
126 timing: Optional[str],
127 notes: Optional[str],
128 date_str: Optional[str],
129) -> None:
130 """Add a supplement entry."""
131 try:
132 storage = ManualLogStorage()
133 target_date = _get_date(date_str)
135 entry = SupplementEntry(
136 time=time,
137 supplement_name=name,
138 dosage=dosage,
139 timing=timing,
140 notes=notes,
141 )
143 storage.add_supplement_entry(target_date, entry)
144 click.echo(f"✅ Added supplement entry for {target_date}: {name}")
146 except Exception as e:
147 click.echo(f"❌ Failed to add supplement entry: {e}", err=True)
148 sys.exit(1)
151@cli.command()
152@click.option("--time", required=True, help="Time of feeling (HH:MM)")
153@click.option("--type", "feeling_type", required=True, help="Type of feeling")
154@click.option("--desc", required=True, help="Description")
155@click.option("--severity", type=int, help="Severity (1-10)")
156@click.option("--loc", "location", help="Body location")
157@click.option("--triggers", help="Possible triggers")
158@click.option("--notes", help="Additional notes")
159@click.option("--date", "date_str", help="Date of entry (YYYY-MM-DD)")
160def feeling(
161 time: str,
162 feeling_type: str,
163 desc: str,
164 severity: Optional[int],
165 location: Optional[str],
166 triggers: Optional[str],
167 notes: Optional[str],
168 date_str: Optional[str],
169) -> None:
170 """Add a body feeling/symptom entry."""
171 try:
172 storage = ManualLogStorage()
173 target_date = _get_date(date_str)
175 entry = BodyFeelingEntry(
176 time=time,
177 feeling_type=feeling_type,
178 description=desc,
179 severity=severity,
180 location=location,
181 triggers=triggers,
182 notes=notes,
183 )
185 storage.add_feeling_entry(target_date, entry)
186 click.echo(f"✅ Added feeling entry for {target_date}: {feeling_type}")
188 except Exception as e:
189 click.echo(f"❌ Failed to add feeling entry: {e}", err=True)
190 sys.exit(1)
193@cli.command()
194@click.option("--mode", required=True, help="Fasting mode (OMAD/PSMF/etc)")
195@click.option("--date", "date_str", help="Date of entry (YYYY-MM-DD)")
196def fasting(mode: str, date_str: Optional[str]) -> None:
197 """Set fasting mode for a day."""
198 try:
199 storage = ManualLogStorage()
200 target_date = _get_date(date_str)
202 storage.set_fasting_mode(target_date, mode)
203 click.echo(f"✅ Set fasting mode for {target_date}: {mode}")
205 except Exception as e:
206 click.echo(f"❌ Failed to set fasting mode: {e}", err=True)
207 sys.exit(1)
210if __name__ == "__main__":
211 cli()