nutrilog 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- nutrilog-0.1.0/.github/workflows/ci.yml +31 -0
- nutrilog-0.1.0/.github/workflows/release.yml +58 -0
- nutrilog-0.1.0/.gitignore +38 -0
- nutrilog-0.1.0/LICENSE +21 -0
- nutrilog-0.1.0/PKG-INFO +282 -0
- nutrilog-0.1.0/PLAN.md +241 -0
- nutrilog-0.1.0/README.md +248 -0
- nutrilog-0.1.0/SKILL.md +116 -0
- nutrilog-0.1.0/nutrilog/__init__.py +3 -0
- nutrilog-0.1.0/nutrilog/auth.py +148 -0
- nutrilog-0.1.0/nutrilog/auth_test.py +120 -0
- nutrilog-0.1.0/nutrilog/cli.py +549 -0
- nutrilog-0.1.0/nutrilog/cli_test.py +241 -0
- nutrilog-0.1.0/nutrilog/client.py +155 -0
- nutrilog-0.1.0/nutrilog/client_test.py +157 -0
- nutrilog-0.1.0/nutrilog/models.py +268 -0
- nutrilog-0.1.0/nutrilog/models_test.py +134 -0
- nutrilog-0.1.0/nutrilog/parser.py +254 -0
- nutrilog-0.1.0/nutrilog/parser_test.py +108 -0
- nutrilog-0.1.0/nutrilog/skill.py +269 -0
- nutrilog-0.1.0/nutrilog/skill_test.py +116 -0
- nutrilog-0.1.0/nutrilog/skills/nutrilog/SKILL.md +116 -0
- nutrilog-0.1.0/nutrilog/storage.py +136 -0
- nutrilog-0.1.0/nutrilog/storage_test.py +89 -0
- nutrilog-0.1.0/pyproject.toml +57 -0
- nutrilog-0.1.0/uv.lock +1081 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: ["main"]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: ["main"]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
name: Test on Python ${{ matrix.python-version }}
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
strategy:
|
|
14
|
+
fail-fast: false
|
|
15
|
+
matrix:
|
|
16
|
+
python-version: ["3.10", "3.11", "3.12", "3.13"]
|
|
17
|
+
|
|
18
|
+
steps:
|
|
19
|
+
- uses: actions/checkout@v4
|
|
20
|
+
|
|
21
|
+
- name: Install uv
|
|
22
|
+
uses: astral-sh/setup-uv@v5
|
|
23
|
+
with:
|
|
24
|
+
version: "latest"
|
|
25
|
+
enable-cache: true
|
|
26
|
+
|
|
27
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
28
|
+
run: uv python install ${{ matrix.python-version }}
|
|
29
|
+
|
|
30
|
+
- name: Run test suite
|
|
31
|
+
run: uv run --python ${{ matrix.python-version }} --extra dev pytest --cov=nutrilog
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
name: Test & Verify Suite
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v4
|
|
14
|
+
|
|
15
|
+
- name: Install uv
|
|
16
|
+
uses: astral-sh/setup-uv@v5
|
|
17
|
+
with:
|
|
18
|
+
version: "latest"
|
|
19
|
+
enable-cache: true
|
|
20
|
+
|
|
21
|
+
- name: Set up Python
|
|
22
|
+
run: uv python install 3.12
|
|
23
|
+
|
|
24
|
+
- name: Run test suite
|
|
25
|
+
run: uv run --extra dev pytest --cov=nutrilog
|
|
26
|
+
|
|
27
|
+
build-and-publish:
|
|
28
|
+
name: Build & Publish to PyPI
|
|
29
|
+
needs: test
|
|
30
|
+
runs-on: ubuntu-latest
|
|
31
|
+
environment:
|
|
32
|
+
name: pypi
|
|
33
|
+
url: https://pypi.org/p/nutrilog
|
|
34
|
+
permissions:
|
|
35
|
+
id-token: write # Required for PyPI Trusted Publishing (OIDC)
|
|
36
|
+
contents: write # Required for GitHub Release creation
|
|
37
|
+
|
|
38
|
+
steps:
|
|
39
|
+
- uses: actions/checkout@v4
|
|
40
|
+
with:
|
|
41
|
+
fetch-depth: 0
|
|
42
|
+
|
|
43
|
+
- name: Install uv
|
|
44
|
+
uses: astral-sh/setup-uv@v5
|
|
45
|
+
with:
|
|
46
|
+
version: "latest"
|
|
47
|
+
|
|
48
|
+
- name: Build distributions (sdist & wheel)
|
|
49
|
+
run: uv build
|
|
50
|
+
|
|
51
|
+
- name: Publish package distributions to PyPI
|
|
52
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
53
|
+
|
|
54
|
+
- name: Create GitHub Release
|
|
55
|
+
uses: softprops/action-gh-release@v2
|
|
56
|
+
with:
|
|
57
|
+
files: dist/*
|
|
58
|
+
generate_release_notes: true
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.so
|
|
6
|
+
.Python
|
|
7
|
+
|
|
8
|
+
# Distribution / packaging
|
|
9
|
+
build/
|
|
10
|
+
dist/
|
|
11
|
+
*.egg-info/
|
|
12
|
+
.eggs/
|
|
13
|
+
|
|
14
|
+
# Virtual environments
|
|
15
|
+
.venv/
|
|
16
|
+
env/
|
|
17
|
+
venv/
|
|
18
|
+
ENV/
|
|
19
|
+
|
|
20
|
+
# Testing & Coverage
|
|
21
|
+
.pytest_cache/
|
|
22
|
+
.coverage
|
|
23
|
+
.coverage.*
|
|
24
|
+
htmlcov/
|
|
25
|
+
|
|
26
|
+
# Local credentials & secrets
|
|
27
|
+
tokens.json
|
|
28
|
+
credentials.json
|
|
29
|
+
client_secrets*.json
|
|
30
|
+
client_secret*.json
|
|
31
|
+
*.token
|
|
32
|
+
|
|
33
|
+
# IDE & OS
|
|
34
|
+
.idea/
|
|
35
|
+
.vscode/
|
|
36
|
+
*.swp
|
|
37
|
+
*.swo
|
|
38
|
+
.DS_Store
|
nutrilog-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Nutrilog Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
nutrilog-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: nutrilog
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A standalone, privacy-first CLI tool for logging meals, macronutrients, and calories directly to Google Health.
|
|
5
|
+
Author: Nutrilog Contributors
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Keywords: calories,cli,fitbit,google-health,health,macros,nutrition
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Environment :: Console
|
|
11
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Topic :: System :: Monitoring
|
|
20
|
+
Classifier: Topic :: Utilities
|
|
21
|
+
Requires-Python: >=3.10
|
|
22
|
+
Requires-Dist: google-auth-oauthlib>=1.2.0
|
|
23
|
+
Requires-Dist: google-auth>=2.28.0
|
|
24
|
+
Requires-Dist: httpx>=0.27.0
|
|
25
|
+
Requires-Dist: pydantic>=2.6.0
|
|
26
|
+
Requires-Dist: python-dateutil>=2.9.0
|
|
27
|
+
Requires-Dist: rich>=13.7.0
|
|
28
|
+
Requires-Dist: typer>=0.12.0
|
|
29
|
+
Provides-Extra: dev
|
|
30
|
+
Requires-Dist: pytest-cov>=5.0.0; extra == 'dev'
|
|
31
|
+
Requires-Dist: pytest>=8.0.0; extra == 'dev'
|
|
32
|
+
Requires-Dist: respx>=0.21.0; extra == 'dev'
|
|
33
|
+
Description-Content-Type: text/markdown
|
|
34
|
+
|
|
35
|
+
# Nutrilog
|
|
36
|
+
|
|
37
|
+
[](https://www.python.org/downloads/)
|
|
38
|
+
[](https://opensource.org/licenses/MIT)
|
|
39
|
+
[](https://typer.tiangolo.com)
|
|
40
|
+
[](https://github.com/astral-sh/uv)
|
|
41
|
+
|
|
42
|
+
A fast, privacy-first CLI tool for logging meals, macronutrients, and calories directly to the **Google Health API (`health.googleapis.com/v4`)** from any terminal. Syncs live with your **Google Health app**, **Fitbit**, and **Pixel Watch**.
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
Today's Nutrition Summary (Mon, Aug 17)
|
|
48
|
+
┏━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━┓
|
|
49
|
+
┃ Time ┃ Meal Type ┃ Food ┃ Protein ┃ Calories ┃ Carbs ┃ Fat ┃
|
|
50
|
+
┡━━━━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━┩
|
|
51
|
+
│ 12:30 PM │ Lunch │ Tofu Edamame Soba Bowl │ 38.5g │ 580 kcal │ 54.0g │ 18.0g │
|
|
52
|
+
│ 04:00 PM │ Snack │ Protein Shake │ 25.0g │ 180 kcal │ 4.0g │ 2.0g │
|
|
53
|
+
└━━━━━━━━━━━━┴━━━━━━━━━━━━━━┴━━━━━━━━━━━━━━━━━━━━━━━━━━┴━━━━━━━━━┴━━━━━━━━━━┴━━━━━━━━━┴━━━━━━━━┘
|
|
54
|
+
╭──────────────────────────────────────────────────────────────────────────────────────────────╮
|
|
55
|
+
│ Daily Total: 63.5g / 120g Protein (53%) | 760 / 2,000 kcal (38%) │
|
|
56
|
+
│ Remaining: 56.5g Protein | 1,240 kcal │
|
|
57
|
+
╰──────────────────────────────────────────────────────────────────────────────────────────────╯
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## ⚡ Quickstart
|
|
63
|
+
|
|
64
|
+
### 1. Run Instantly with `uvx` (No Installation Required)
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
# Log a meal using intuitive shorthand syntax (in dry-run preview)
|
|
68
|
+
uvx --from . nutrilog "38p 18f 54c 580k Tofu Edamame Soba Bowl" --dry-run
|
|
69
|
+
|
|
70
|
+
# View today's summary & daily target progress
|
|
71
|
+
uvx --from . nutrilog today
|
|
72
|
+
|
|
73
|
+
# View command help
|
|
74
|
+
uvx --from . nutrilog --help
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### 2. Install Globally with `uv` or `pip`
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
# Install globally via uv
|
|
81
|
+
uv tool install .
|
|
82
|
+
|
|
83
|
+
# Or via standard pip
|
|
84
|
+
pip install .
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## 🏗️ Architecture & Cloud Sync
|
|
90
|
+
|
|
91
|
+
```mermaid
|
|
92
|
+
graph LR
|
|
93
|
+
User["User Terminal"] --> CLI["nutrilog CLI<br/>(Typer + Rich)"]
|
|
94
|
+
CLI --> Parser["Macro & Shorthand<br/>Regex Parser"]
|
|
95
|
+
CLI --> Auth["OAuth 2.0 Auth Manager<br/>(PKCE / Loopback)"]
|
|
96
|
+
Auth --> Keyring["Local Token Store<br/>(~/.config/nutrilog/tokens.json)"]
|
|
97
|
+
CLI --> Client["Google Health Client<br/>(health.googleapis.com/v4)"]
|
|
98
|
+
Client --> GoogleHealth["Google Health Platform<br/>(Pixel Watch / Fitbit / Mobile App)"]
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
- **Zero-Friction Logging:** Log any meal in $<2$ seconds directly from your command line.
|
|
102
|
+
- **Hardware & Cloud Sync:** Data writes directly to Google Health API and syncs to your Pixel Watch, Fitbit, and phone dashboard.
|
|
103
|
+
- **Local & Private:** Auth tokens are stored locally on your machine with strict `0600` permissions.
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## 🚀 Usage & Commands
|
|
108
|
+
|
|
109
|
+
### 1. Shorthand Meal Logging
|
|
110
|
+
|
|
111
|
+
Macros and calorie tokens can appear anywhere in the string in any order:
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
# Shorthand notation (protein 'p', fat 'f', carbs 'c', calories 'k' or 'cal')
|
|
115
|
+
nutrilog "38p 18f 54c 580k Tofu Edamame Soba Bowl"
|
|
116
|
+
|
|
117
|
+
# Explicit units and labels
|
|
118
|
+
nutrilog "Grilled Salmon protein: 35g, fat: 12g, carbs: 5g, calories: 280, fiber: 2g"
|
|
119
|
+
|
|
120
|
+
# Prefix notation
|
|
121
|
+
nutrilog "p30 f10 c45 390cal Chicken Burrito Bowl"
|
|
122
|
+
|
|
123
|
+
# Automatic calorie calculation if calories are omitted (4*P + 4*C + 9*F)
|
|
124
|
+
nutrilog "30p 40c 10f Oatmeal"
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
#### Shorthand Syntax Cheat-Sheet
|
|
128
|
+
|
|
129
|
+
| Nutrient | Recognized Formats |
|
|
130
|
+
| :--- | :--- |
|
|
131
|
+
| **Protein** | `38p`, `p38`, `38g protein`, `protein: 38g`, `pro: 38` |
|
|
132
|
+
| **Fat** | `18f`, `f18`, `18g fat`, `fat: 18g`, `total_fat: 18` |
|
|
133
|
+
| **Carbohydrates** | `54c`, `c54`, `54g carbs`, `carbs: 54g`, `carb: 54` |
|
|
134
|
+
| **Calories / Energy**| `580k`, `580cal`, `580kcal`, `cal: 580`, `calories: 580` |
|
|
135
|
+
| **Fiber** | `9fib`, `9g fiber`, `fiber: 9g` |
|
|
136
|
+
| **Sugar** | `5sug`, `5g sugar`, `sugar: 5g` |
|
|
137
|
+
| **Sodium** | `500mg sod`, `sodium: 0.5g` |
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
### 2. Flag-Based Logging
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
# Explicit flags
|
|
145
|
+
nutrilog log "Grilled Barramundi & Veggies" \
|
|
146
|
+
--protein 36 \
|
|
147
|
+
--calories 480 \
|
|
148
|
+
--fat 14 \
|
|
149
|
+
--carbs 12 \
|
|
150
|
+
--meal lunch
|
|
151
|
+
|
|
152
|
+
# Quick macro top-up (e.g. protein shake, snack)
|
|
153
|
+
nutrilog quick --protein 25 --calories 180 --name "Post-Workout Protein Shake"
|
|
154
|
+
|
|
155
|
+
# Dry run (preview payload without sending)
|
|
156
|
+
nutrilog "35p 450k Protein Shake" --dry-run
|
|
157
|
+
|
|
158
|
+
# Output raw JSON payload
|
|
159
|
+
nutrilog "35p 450k Protein Shake" --json
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
### 3. Reviewing Today's Totals, History & Listing Meals
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
# View today's rollup vs daily targets
|
|
168
|
+
nutrilog today
|
|
169
|
+
|
|
170
|
+
# List recent meals with their Data Point IDs
|
|
171
|
+
nutrilog list --days 3
|
|
172
|
+
|
|
173
|
+
# View past week's meal history
|
|
174
|
+
nutrilog history --days 7 --ids
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
179
|
+
### 4. Deleting Meals
|
|
180
|
+
|
|
181
|
+
Delete mistakenly logged or duplicate meals using their Data Point ID:
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
# Delete with confirmation prompt
|
|
185
|
+
nutrilog delete <DATA_POINT_ID>
|
|
186
|
+
|
|
187
|
+
# Delete immediately (skip prompt)
|
|
188
|
+
nutrilog delete <DATA_POINT_ID> --yes
|
|
189
|
+
# Or alias
|
|
190
|
+
nutrilog rm <DATA_POINT_ID> -y
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
### 5. Configuring Daily Nutrition Targets
|
|
196
|
+
|
|
197
|
+
```bash
|
|
198
|
+
# Display active daily targets
|
|
199
|
+
nutrilog config show
|
|
200
|
+
|
|
201
|
+
# Set custom daily macro and calorie targets
|
|
202
|
+
nutrilog config set --calories 2200 --protein 140 --carbs 220 --fat 65
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
## 🤖 AI Agent Skill Integration
|
|
208
|
+
|
|
209
|
+
Nutrilog includes a packaged **Agent Skill** (`SKILL.md`) that allows AI coding assistants (Gemini, Claude Code, Cursor, Antigravity) to discover and run Nutrilog commands autonomously.
|
|
210
|
+
|
|
211
|
+
```bash
|
|
212
|
+
# Check skill status across detected AI tools
|
|
213
|
+
nutrilog skill status
|
|
214
|
+
|
|
215
|
+
# Install into default shared location (~/.agents/skills)
|
|
216
|
+
nutrilog skill install
|
|
217
|
+
|
|
218
|
+
# Install into all detected agent tool directories
|
|
219
|
+
nutrilog skill install --all
|
|
220
|
+
|
|
221
|
+
# Symlink instead of copying (for live package updates)
|
|
222
|
+
nutrilog skill install --all --link
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
## 🔑 Google Cloud Authentication (`nutrilog auth`)
|
|
228
|
+
|
|
229
|
+
Nutrilog connects directly to the Google Health API using OAuth 2.0.
|
|
230
|
+
|
|
231
|
+
### Step 1: GCP Project Setup (Free, 1-time)
|
|
232
|
+
|
|
233
|
+
1. Create or select a project in [Google Cloud Console](https://console.cloud.google.com).
|
|
234
|
+
2. Enable the **Google Health API** (*APIs & Services $\rightarrow$ Library $\rightarrow$ Google Health API $\rightarrow$ Enable*).
|
|
235
|
+
3. Under **OAuth consent screen**, select **External**, and add your email to **Test Users**.
|
|
236
|
+
4. Under **Credentials**, create an **OAuth client ID** of type **Desktop App**, and download the JSON credentials.
|
|
237
|
+
|
|
238
|
+
### Step 2: Configure & Log In
|
|
239
|
+
|
|
240
|
+
```bash
|
|
241
|
+
# Option A: Import client_secrets.json file
|
|
242
|
+
nutrilog auth setup --file path/to/client_secrets.json
|
|
243
|
+
|
|
244
|
+
# Option B: Pass credentials via CLI flags
|
|
245
|
+
nutrilog auth setup --client-id "<YOUR_CLIENT_ID>" --client-secret "<YOUR_CLIENT_SECRET>"
|
|
246
|
+
|
|
247
|
+
# Option C: Use environment variables
|
|
248
|
+
export NUTRILOG_CLIENT_ID="<YOUR_CLIENT_ID>"
|
|
249
|
+
export NUTRILOG_CLIENT_SECRET="<YOUR_CLIENT_SECRET>"
|
|
250
|
+
|
|
251
|
+
# Complete browser OAuth login
|
|
252
|
+
nutrilog auth login
|
|
253
|
+
|
|
254
|
+
# Check authentication status & token expiry
|
|
255
|
+
nutrilog auth status
|
|
256
|
+
|
|
257
|
+
# Sign out & clear local tokens
|
|
258
|
+
nutrilog auth logout
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
---
|
|
262
|
+
|
|
263
|
+
## 🧪 Development & Testing
|
|
264
|
+
|
|
265
|
+
Each Python module has a corresponding `_test.py` unit test suite alongside it:
|
|
266
|
+
|
|
267
|
+
```bash
|
|
268
|
+
# Set up virtual environment and install dependencies
|
|
269
|
+
uv venv
|
|
270
|
+
uv pip install -e ".[dev]"
|
|
271
|
+
|
|
272
|
+
# Run full test suite (57 tests)
|
|
273
|
+
uv run pytest
|
|
274
|
+
|
|
275
|
+
# Run tests with coverage report
|
|
276
|
+
uv run pytest --cov=nutrilog
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
---
|
|
280
|
+
|
|
281
|
+
## 📄 License
|
|
282
|
+
MIT License. See [LICENSE](LICENSE) for details.
|
nutrilog-0.1.0/PLAN.md
ADDED
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
# Nutrilog: End-to-End Implementation & Publishing Plan
|
|
2
|
+
|
|
3
|
+
A standalone, privacy-first CLI tool for logging meals, macronutrients, and calories directly to the **Google Health API (`health.googleapis.com/v4`)** from any terminal, usable with any standard `@gmail.com` or Google Workspace account.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. Executive Architecture Overview
|
|
8
|
+
|
|
9
|
+
```mermaid
|
|
10
|
+
graph LR
|
|
11
|
+
User["User Terminal"] --> CLI["nutrilog CLI<br/>(Click / Typer)"]
|
|
12
|
+
CLI --> Parser["Macro & Timestamp<br/>Parser"]
|
|
13
|
+
CLI --> Auth["OAuth 2.0 Auth Manager<br/>(PKCE / Local Loopback)"]
|
|
14
|
+
Auth --> Keyring["Local Token Store<br/>(~/.config/nutrilog/tokens.json)"]
|
|
15
|
+
CLI --> Client["Google Health Client<br/>(health.googleapis.com/v4)"]
|
|
16
|
+
Client --> GoogleHealth["Google Health Platform<br/>(Google Health App / Pixel Watch / Fitbit)"]
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### Core Goals:
|
|
20
|
+
1. **Zero-Friction Logging:** Log any meal (cafe, restaurant, home-cooked) in $<2$ seconds via shorthand or natural flags.
|
|
21
|
+
2. **True Cloud Sync:** Syncs natively with the user's Pixel Watch, Fitbit, and Google Health mobile app.
|
|
22
|
+
3. **Works Anywhere:** Completely decoupled from internal corporate tooling (`google3`), packaged as a clean open-source Python tool (`pip` / `pipx`).
|
|
23
|
+
4. **Google Cloud Publishing Path:** Clear roadmap from personal developer mode ("Testing" state) to verified published OAuth app.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## 2. Google Cloud & OAuth 2.0 Setup (Prerequisites & Publishing)
|
|
28
|
+
|
|
29
|
+
Because health and nutrition data is categorized under Google's **Sensitive / Restricted Scopes**, Google applies specific verification tiers for OAuth applications.
|
|
30
|
+
|
|
31
|
+
### Phase 2.1: Google Cloud Project Creation
|
|
32
|
+
1. Create a project in [Google Cloud Console](https://console.cloud.google.com): `nutrilog-cli-app`.
|
|
33
|
+
2. Enable the **Google Health API**:
|
|
34
|
+
* Navigate to *APIs & Services > Library*.
|
|
35
|
+
* Search for **Google Health API** and click **Enable**.
|
|
36
|
+
|
|
37
|
+
### Phase 2.2: OAuth 2.0 Consent Screen Configuration
|
|
38
|
+
1. **User Type:** Select **External** (allows any `@gmail.com` account to sign in).
|
|
39
|
+
2. **App Information:**
|
|
40
|
+
* App Name: `Nutrilog CLI`
|
|
41
|
+
* User support email: Developer email.
|
|
42
|
+
* App logo (optional, 120x120px).
|
|
43
|
+
3. **Scopes Requested:**
|
|
44
|
+
* `https://www.googleapis.com/auth/health.nutrition.writeonly` (Required: write meals, calories, macros)
|
|
45
|
+
* `https://www.googleapis.com/auth/health.nutrition.readonly` (Optional: view meal history)
|
|
46
|
+
4. **OAuth Client Credentials:**
|
|
47
|
+
* Create Credentials > **OAuth Client ID**.
|
|
48
|
+
* Application Type: **Desktop App**.
|
|
49
|
+
* Name: `Nutrilog Desktop Client`.
|
|
50
|
+
* Download `client_secrets.json`.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
### Phase 2.3: The Verification & Publishing Roadmap
|
|
55
|
+
|
|
56
|
+
Google enforces specific trust states for OAuth applications accessing Health APIs:
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
[ State A: Local Developer Mode ]
|
|
60
|
+
│ (Self-use & Test Users)
|
|
61
|
+
▼
|
|
62
|
+
[ State B: Pre-Launch / Test Track ] ──► Up to 100 explicit test Gmail accounts
|
|
63
|
+
│ (Instant access, zero review required)
|
|
64
|
+
▼
|
|
65
|
+
[ State C: Public Production App ] ──► Cloud Verification + CASA Assessment
|
|
66
|
+
(Publicly usable by anyone on the internet)
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
| Deployment Tier | Who Can Sign In | Requirements | Best For |
|
|
70
|
+
| :--- | :--- | :--- | :--- |
|
|
71
|
+
| **Tier 1: Developer / Testing** *(Fastest)* | You + up to 100 explicitly added test Gmail accounts. | • Add test emails in GCP Console.<br/>• Users see a one-time *"Google hasn't verified this app"* warning screen. | **Immediate personal use, pair programming, beta testers.** |
|
|
72
|
+
| **Tier 2: Publicly Verified App** | Any Google account worldwide without warning dialogs. | • Public domain with verified ownership.<br/>• Privacy Policy & Terms of Service URLs.<br/>• YouTube demo video showing OAuth flow.<br/>• Cloud Application Security Assessment (CASA Tier 2). | **Public PyPI release for widespread distribution.** |
|
|
73
|
+
|
|
74
|
+
> [!TIP]
|
|
75
|
+
> **Recommended Strategy:** Start in **Tier 1 (Testing Mode)** with your personal Gmail added to the Test Users list. This allows full, unrestricted API access within 5 minutes while working towards Tier 2 verification for public PyPI distribution.
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## 3. Google Health API (v4) Technical Specification
|
|
80
|
+
|
|
81
|
+
### 3.1 Endpoint Schema
|
|
82
|
+
* **URL:** `POST https://health.googleapis.com/v4/users/me/dataTypes/nutrition-log/dataPoints`
|
|
83
|
+
* **HTTP Method:** `POST`
|
|
84
|
+
* **Headers:**
|
|
85
|
+
```http
|
|
86
|
+
Authorization: Bearer <ACCESS_TOKEN>
|
|
87
|
+
Content-Type: application/json
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### 3.2 Canonical JSON Request Payload
|
|
91
|
+
```json
|
|
92
|
+
{
|
|
93
|
+
"nutritionLog": {
|
|
94
|
+
"foodDisplayName": "Tofu & Edamame Soba Bowl",
|
|
95
|
+
"mealType": "LUNCH",
|
|
96
|
+
"interval": {
|
|
97
|
+
"startTime": "2026-08-17T12:30:00Z",
|
|
98
|
+
"endTime": "2026-08-17T13:00:00Z"
|
|
99
|
+
},
|
|
100
|
+
"energy": {
|
|
101
|
+
"kcal": 580
|
|
102
|
+
},
|
|
103
|
+
"totalCarbohydrate": {
|
|
104
|
+
"grams": 54.0
|
|
105
|
+
},
|
|
106
|
+
"totalFat": {
|
|
107
|
+
"grams": 18.0
|
|
108
|
+
},
|
|
109
|
+
"nutrients": [
|
|
110
|
+
{
|
|
111
|
+
"nutrient": "PROTEIN",
|
|
112
|
+
"quantity": {
|
|
113
|
+
"grams": 38.5
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
"nutrient": "FIBER",
|
|
118
|
+
"quantity": {
|
|
119
|
+
"grams": 9.0
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
"nutrient": "SODIUM",
|
|
124
|
+
"quantity": {
|
|
125
|
+
"grams": 0.650
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
],
|
|
129
|
+
"serving": {
|
|
130
|
+
"amount": 1.0,
|
|
131
|
+
"unit": "bowl"
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### 3.3 Supported Nutrients & Enums
|
|
138
|
+
* **Meal Types:** `MEAL_TYPE_UNSPECIFIED`, `BREAKFAST`, `LUNCH`, `DINNER`, `SNACK`.
|
|
139
|
+
* **Nutrient Enums:** `PROTEIN`, `TOTAL_FAT`, `TOTAL_CARBOHYDRATE`, `FIBER`, `SUGAR`, `SODIUM`, `POTASSIUM`, `CALCIUM`, `IRON`, `SATURATED_FAT`, `CHOLESTEROL`.
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## 4. Local CLI Implementation Architecture
|
|
144
|
+
|
|
145
|
+
```
|
|
146
|
+
~/Sandbox/nutrilog/
|
|
147
|
+
├── pyproject.toml # Build & dependency metadata (Hatchling / Flit)
|
|
148
|
+
├── README.md # Quickstart, setup guide & documentation
|
|
149
|
+
├── nutrilog/
|
|
150
|
+
│ ├── __init__.py
|
|
151
|
+
│ ├── cli.py # CLI command dispatch & argument parsing (Typer/Click)
|
|
152
|
+
│ ├── auth.py # OAuth 2.0 PKCE local loopback flow & token refresh
|
|
153
|
+
│ ├── client.py # Google Health API v4 HTTP client
|
|
154
|
+
│ ├── parser.py # Shorthand & natural language macro parser
|
|
155
|
+
│ ├── storage.py # Local secure token & cache management
|
|
156
|
+
│ └── models.py # Pydantic data schemas for meals & nutrients
|
|
157
|
+
└── tests/
|
|
158
|
+
├── test_parser.py # Unit tests for shorthand macro syntax
|
|
159
|
+
├── test_client.py # Mocked HTTP tests for API payloads
|
|
160
|
+
└── test_auth.py # Token refresh & keyring tests
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## 5. User Experience & CLI Command Design
|
|
166
|
+
|
|
167
|
+
### 5.1 Initial Setup (`nutrilog auth`)
|
|
168
|
+
```bash
|
|
169
|
+
# Authenticate with Google OAuth via browser loopback
|
|
170
|
+
nutrilog auth login
|
|
171
|
+
|
|
172
|
+
# Check authentication status & token expiry
|
|
173
|
+
nutrilog auth status
|
|
174
|
+
|
|
175
|
+
# Discard tokens & sign out
|
|
176
|
+
nutrilog auth logout
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### 5.2 Fast Shorthand Logging (`nutrilog log` / `nutrilog quick`)
|
|
180
|
+
```bash
|
|
181
|
+
# 1. Shorthand notation (macros + name)
|
|
182
|
+
nutrilog "38p 18f 54c 580k Tofu Edamame Soba Bowl"
|
|
183
|
+
|
|
184
|
+
# 2. Flag-based explicit logging
|
|
185
|
+
nutrilog log "Grilled Barramundi & Veggies" \
|
|
186
|
+
--protein 36 \
|
|
187
|
+
--calories 480 \
|
|
188
|
+
--fat 14 \
|
|
189
|
+
--carbs 12 \
|
|
190
|
+
--meal lunch
|
|
191
|
+
|
|
192
|
+
# 3. Quick calorie/protein top-up
|
|
193
|
+
nutrilog quick --protein 25 --calories 180 --name "Post-workout Protein Shake"
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### 5.3 Meal History & Daily Targets (`nutrilog today` / `nutrilog history`)
|
|
197
|
+
```bash
|
|
198
|
+
# View today's total logged macros vs daily targets
|
|
199
|
+
nutrilog today
|
|
200
|
+
|
|
201
|
+
# Output:
|
|
202
|
+
# ━━━━━━━━━━━━━━━━ Today's Nutrition Summary (Mon, Aug 17) ━━━━━━━━━━━━━━━━
|
|
203
|
+
# Meals Logged: 2
|
|
204
|
+
# • 12:30 PM [Lunch] Tofu & Edamame Bowl 38.5g P | 580 kcal | 54g C | 18g F
|
|
205
|
+
# • 04:00 PM [Snack] Protein Shake 25.0g P | 180 kcal | 4g C | 2g F
|
|
206
|
+
# ──────────────────────────────────────────────────────────────────────────
|
|
207
|
+
# Daily Total: 63.5g / 120g Protein (53%) | 760 / 2,000 kcal (38%)
|
|
208
|
+
# Remaining: 56.5g Protein | 1,240 kcal
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
## 6. Detailed Step-by-Step Implementation Roadmap
|
|
214
|
+
|
|
215
|
+
### Step 1: GCP Project & OAuth Setup
|
|
216
|
+
* [ ] Create GCP Project: `nutrilog-prod`.
|
|
217
|
+
* [ ] Enable `Google Health API` in API Library.
|
|
218
|
+
* [ ] Configure OAuth Consent Screen (External, Test mode).
|
|
219
|
+
* [ ] Add personal Gmail to **Test Users**.
|
|
220
|
+
* [ ] Download Desktop OAuth Client JSON to `~/.config/nutrilog/credentials.json`.
|
|
221
|
+
|
|
222
|
+
### Step 2: Core Python Engine (`nutrilog/`)
|
|
223
|
+
* [ ] **`auth.py`**: Implement `InstalledAppFlow.run_local_server(port=0)` with automatic token refresh via `google.auth.transport.requests.Request`.
|
|
224
|
+
* [ ] **`storage.py`**: Secure token persistence in `~/.config/nutrilog/tokens.json` with strict `0600` permissions.
|
|
225
|
+
* [ ] **`models.py`**: Define `MealLog`, `NutrientEntry`, and `MacroSummary` dataclasses.
|
|
226
|
+
* [ ] **`parser.py`**: Implement regex parser for shorthand macro strings (e.g. `35p 600k 20f 40c`).
|
|
227
|
+
* [ ] **`client.py`**: Build resilient `GoogleHealthClient` handling retry logic, ISO timestamp formatting, and payload serialization.
|
|
228
|
+
|
|
229
|
+
### Step 3: CLI Interface & Rich Terminal UI
|
|
230
|
+
* [ ] Build Typer CLI commands in `cli.py` (`login`, `log`, `today`, `history`, `config`).
|
|
231
|
+
* [ ] Format terminal tables and progress bars using `rich`.
|
|
232
|
+
|
|
233
|
+
### Step 4: Verification & Integration Testing
|
|
234
|
+
* [ ] Run end-to-end integration test: log a test meal from terminal.
|
|
235
|
+
* [ ] Open **Google Health App on phone / Pixel Watch / Fitbit** and confirm live synchronization.
|
|
236
|
+
* [ ] Verify accurate calorie rollup and macronutrient calculation on mobile dashboard.
|
|
237
|
+
|
|
238
|
+
### Step 5: Packaging & Open-Source Distribution
|
|
239
|
+
* [ ] Configure `pyproject.toml` with entry point `nutrilog = "nutrilog.cli:app"`.
|
|
240
|
+
* [ ] Author comprehensive `README.md` with GCP setup walkthrough and visual examples.
|
|
241
|
+
* [ ] Publish package to PyPI (`pip install nutrilog`).
|