drunken-chunk 1.0.0

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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Antigravity Team
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.
package/README.md ADDED
@@ -0,0 +1,266 @@
1
+ # πŸ’§ Drunken Chunk (drunken-chunk)
2
+
3
+ [![Python Version](https://img.shields.io/badge/python-3.8%2B-blue.svg)](https://www.python.org/)
4
+ [![License](https://img.shields.io/badge/license-MIT-green.svg)](https://opensource.org/licenses/MIT)
5
+ [![Platform](https://img.shields.io/badge/platform-windows%20%7C%20macos%20%7C%20linux-lightgrey.svg)](#-supported-platforms--native-mechanics)
6
+ [![Dependencies](https://img.shields.io/badge/dependencies-zero-orange.svg)](#-technical-implementation-highlights)
7
+
8
+ A beautiful, premium, cross-platform CLI-based water tracking tool, smart background reminder service, and behavioral analytics dashboard. Written in 100% pure Python with **zero external dependencies**, Drunken Chunk leverages native OS mechanisms to keep you healthy, hydrated, and productive.
9
+
10
+ ---
11
+
12
+ ## πŸ“– Table of Contents
13
+
14
+ - [✨ Features](#-features)
15
+ - [πŸ–₯️ Supported Platforms & Native Mechanics](#%EF%B8%8F-supported-platforms--native-mechanics)
16
+ - [πŸš€ Installation & Quick Start](#-installation--quick-start)
17
+ - [Prerequisites](#prerequisites)
18
+ - [Global Installation (Recommended)](#global-installation-recommended)
19
+ - [Local Development & Source Running](#local-development--source-running)
20
+ - [πŸ› οΈ CLI Commands & Usage](#%EF%B8%8F-cli-commands--usage)
21
+ - [1. Initialization (`init`)](#1-initialization-init)
22
+ - [2. Interactive Console Logging (`log` / `log-custom`)](#2-interactive-console-logging-log--log-custom)
23
+ - [3. Dashboard Progress (`status`)](#3-dashboard-progress-status)
24
+ - [4. Vessel Shortcuts (`shortcut`)](#4-vessel-shortcuts-shortcut)
25
+ - [5. Historical Log View (`history`)](#5-historical-log-view-history)
26
+ - [6. Behavioral Analytics (`analyze`)](#6-behavioral-analytics-analyze)
27
+ - [7. Reminder Controls (`start` / `stop` / `snooze`)](#7-reminder-controls-start--stop--snooze)
28
+ - [8. Startup Integration (`autostart`)](#8-startup-integration-autostart)
29
+ - [πŸ“‚ Project Directory Layout](#-project-directory-layout)
30
+ - [βš™οΈ How It Works under the Hood](#%EF%B8%8F-how-it-works-under-the-hood)
31
+ - [🀝 Contributing](#-contributing)
32
+ - [πŸ“„ License](#-license)
33
+
34
+ ---
35
+
36
+ ## ✨ Features
37
+
38
+ * **Smart Reminder Service**: Notifies you to drink water based on your waking hours and consumption frequency. Unlike rigid timer tools, Drunken Chunk is **context-aware**β€”logging a drink automatically pushes your next notification out by one full interval.
39
+ * **Rich Terminal UI**: Beautiful, premium gradient styling, custom ASCII banners, and dynamic progress bars without requiring external packages like `rich` or `colorama`.
40
+ * **Platform-Native Notifications**: Zero-dependency notifications that natively slide in on Windows (WinRT toast alerts), macOS (AppleScript alerts), and Linux (D-Bus `notify-send` alerts).
41
+ * **Behavioral Telemetry**: Tracks your hydration rates across weekdays, details diurnal drinking spreads (morning, afternoon, evening), calculates target consistency indices, and surfaces actionable sleep/health recommendations.
42
+ * **SQL Log Storage**: Leverages a local, lightweight SQLite database to manage user dimensions, drink vessel shortcuts, logging history, and daemon state.
43
+
44
+ ---
45
+
46
+ ## πŸ–₯️ Supported Platforms & Native Mechanics
47
+
48
+ Drunken Chunk is built to operate with lightweight, native OS components instead of heavy third-party system libraries:
49
+
50
+ | Platform | Native Desktop Notifications | Background Daemon Detaching | Autostart Boot Entry |
51
+ | :--- | :--- | :--- | :--- |
52
+ | πŸͺŸ **Windows** | PowerShell WinRT XML API Toast | `DETACHED_PROCESS` + `CREATE_NO_WINDOW` flags | Windows Startup batch launcher script (`shell:startup`) |
53
+ | 🍎 **macOS** | AppleScript `display notification` | `start_new_session` (`setsid`) background process | User Launch Agent (`~/Library/LaunchAgents/*.plist`) |
54
+ | 🐧 **Linux** | Desktop D-Bus `notify-send` utility | `start_new_session` (`setsid`) background process | XDG Desktop Entry (`~/.config/autostart/*.desktop`) |
55
+
56
+ ---
57
+
58
+ ## πŸš€ Installation & Quick Start
59
+
60
+ ### Prerequisites
61
+ * Python **3.8** or newer installed.
62
+ * Standard command-line terminal environment.
63
+
64
+ ### Global Installation (Recommended)
65
+ You can install Drunken Chunk in editable mode or globally from source. Open your terminal in this repository folder and run:
66
+
67
+ ```bash
68
+ pip install -e .
69
+ ```
70
+
71
+ Once installed, the main command is linked globally. Test the installation by running:
72
+ ```bash
73
+ drunken-chunk --help
74
+ ```
75
+
76
+ ### Local Development & Source Running
77
+ If you prefer not to install the package globally, you can invoke the CLI using the platform-specific execution wrappers located at the repository root:
78
+
79
+ * **Windows PowerShell/Command Prompt:**
80
+ ```cmd
81
+ drunken-chunk.bat --help
82
+ ```
83
+ * **macOS / Linux terminal:**
84
+ Ensure the wrapper script has execute permissions:
85
+ ```bash
86
+ chmod +x drunken-chunk
87
+ ./drunken-chunk --help
88
+ ```
89
+
90
+ ---
91
+
92
+ ## πŸ› οΈ CLI Commands & Usage
93
+
94
+ ### 1. Initialization (`init`)
95
+ Sets up your personal physical profile and hydration constraints. Drunken Chunk will calculate a customized daily intake target based on your metabolic demands (weight, height, and BMI metadata).
96
+ ```bash
97
+ drunken-chunk init
98
+ ```
99
+ During initialization, you will be prompted for:
100
+ 1. Your name.
101
+ 2. Weight in kg (for metabolic calculation).
102
+ 3. Height in cm (for BMI classification).
103
+ 4. Wake-up and Sleep times (defines **Quiet Hours** to prevent middle-of-the-night notifications).
104
+ 5. Notification interval in minutes.
105
+ 6. Optional custom target override (e.g. 3000 ml).
106
+
107
+ > [!NOTE]
108
+ > **Automatic Setup Trigger**: If you run any other CLI subcommand on a fresh database before running `init`, the program will automatically launch the setup wizard first to ensure your database is initialized.
109
+
110
+ ---
111
+
112
+ ### 2. Interactive Console Logging (`log` / `log-custom`)
113
+ Log your water intake instantly using direct integers, custom shortcuts, or relative timestamps.
114
+
115
+ * **Log by Volume (milliliters):**
116
+ ```bash
117
+ drunken-chunk log 350
118
+ ```
119
+ * **Log using a Vessel Shortcut:**
120
+ ```bash
121
+ drunken-chunk log glass
122
+ ```
123
+ * **Log in the Past (Relative/Absolute):**
124
+ Useful if you forgot to log a drink earlier:
125
+ ```bash
126
+ drunken-chunk log-custom 250 "15m ago" # 15 minutes ago
127
+ drunken-chunk log-custom 500 "2h ago" # 2 hours ago
128
+ drunken-chunk log-custom 300 "14:30" # At exactly 2:30 PM today
129
+ ```
130
+ * **Undo Last Log Entry:**
131
+ Quickly undo your last recorded log if you made a typo:
132
+ ```bash
133
+ drunken-chunk undo
134
+ ```
135
+
136
+ ---
137
+
138
+ ### 3. Dashboard Progress (`status`)
139
+ Presents a status breakdown including your hydration progress bar, current intake totals, hydration level metrics, and notification service status.
140
+ ```bash
141
+ drunken-chunk status
142
+ ```
143
+
144
+ * **Compact Prompts & Status Bars (`--mini`):**
145
+ Perfect for loading into status widgets (like tmux, polybar, or zsh prompts):
146
+ ```bash
147
+ drunken-chunk status --mini
148
+ ```
149
+ *Output Example:* `[β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–‘β–‘β–‘β–‘β–‘β–‘] 66.7%`
150
+
151
+ ---
152
+
153
+ ### 4. Vessel Shortcuts (`shortcut`)
154
+ Configure convenient, memorable shortcuts for drink vessels to speed up logging.
155
+
156
+ * **List active vessel shortcuts:**
157
+ ```bash
158
+ drunken-chunk shortcut list
159
+ ```
160
+ * **Add or update a shortcut (e.g., custom `mug` holding 300ml):**
161
+ ```bash
162
+ drunken-chunk shortcut add mug 300
163
+ ```
164
+ * **Remove a shortcut:**
165
+ ```bash
166
+ drunken-chunk shortcut remove bottle
167
+ ```
168
+
169
+ ---
170
+
171
+ ### 5. Historical Log View (`history`)
172
+ Review your logged intake and check if targets were reached over the past week.
173
+ ```bash
174
+ drunken-chunk history --days 7
175
+ ```
176
+
177
+ ---
178
+
179
+ ### 6. Behavioral Analytics (`analyze`)
180
+ Executes behavior analysis on your historical SQL registry to map habits and discover dehydration triggers.
181
+ ```bash
182
+ drunken-chunk analyze
183
+ ```
184
+ Surfaces telemetry analytics for:
185
+ * **Target Consistency**: Hit rates and consistency indices over the last fortnight.
186
+ * **Temporal Spread**: Diurnal breakdown showing what percentage of water you log in the Morning, Afternoon, Evening, and Night.
187
+ * **Weekday Variance**: Visualizes which days of the week your intake drops.
188
+ * **Intake Frequency Gaps**: Calculates the average time gap between drinks to determine if you are "chugging" vs "sipping".
189
+ * **Personalized Health Warnings**: Actionable alerts based on drinking patterns (e.g. drinking too close to bed).
190
+
191
+ ---
192
+
193
+ ### 7. Reminder Controls (`start` / `stop` / `snooze`)
194
+ Manage the background notification process.
195
+
196
+ * **Start the background daemon:**
197
+ ```bash
198
+ drunken-chunk start
199
+ ```
200
+ * **Stop the background daemon:**
201
+ ```bash
202
+ drunken-chunk stop
203
+ ```
204
+ * **Snooze reminders (e.g., temporarily silence popups for 45 minutes):**
205
+ ```bash
206
+ drunken-chunk snooze 45
207
+ ```
208
+
209
+ ---
210
+
211
+ ### 8. Startup Integration (`autostart`)
212
+ Configure the background hydration daemon to boot up automatically when your computer starts.
213
+
214
+ * **Toggle or check autostart registration:**
215
+ ```bash
216
+ drunken-chunk autostart status
217
+ drunken-chunk autostart enable
218
+ drunken-chunk autostart disable
219
+ ```
220
+
221
+ ---
222
+
223
+ ## πŸ“‚ Project Directory Layout
224
+
225
+ ```
226
+ drunken-chunk/
227
+ β”œβ”€β”€ drunken_chunk/
228
+ β”‚ β”œβ”€β”€ __init__.py
229
+ β”‚ β”œβ”€β”€ analytics.py # Telemetry calculations and diurnal spreading analysis
230
+ β”‚ β”œβ”€β”€ cli.py # CLI argument parser and routing commands
231
+ β”‚ β”œβ”€β”€ db.py # SQLite database setup and profile/log queries
232
+ β”‚ β”œβ”€β”€ profile.py # Metabolic calculations and BMI indexing
233
+ β”‚ β”œβ”€β”€ reminder.py # Cross-platform background daemon and notification dispatching
234
+ β”‚ β”œβ”€β”€ shortcut.py # Shortcut validation and registry updates
235
+ β”‚ └── utils.py # ANSI colors, banners, and table visual formatters
236
+ β”œβ”€β”€ setup.py # Package metadata and installation script
237
+ β”œβ”€β”€ drunken-chunk # Linux & macOS shell executable wrapper
238
+ β”œβ”€β”€ drunken-chunk.bat # Windows cmd batch executable wrapper
239
+ β”œβ”€β”€ README.md # User manual and documentation
240
+ └── .gitignore
241
+ ```
242
+
243
+ ---
244
+
245
+ ## βš™οΈ How It Works under the Hood
246
+
247
+ * **Data Isolation**: User profiles, custom shortcuts, daemon states, and hydration registries are stored securely in a local directory `~/.drunken_chunk/drunken_chunk.db`.
248
+ * **Zero-Library Color Sequences**: Windows Console colors are configured using standard ANSI escapes combined with native Win32 `ctypes` bindings that enable Virtual Terminal Processing on Windows consoles. Non-Windows systems support standard ANSI styling natively.
249
+ * **Low Overhead Polling Loop**: The reminder daemon process runs a lightweight background loop polling every 20 seconds. It reads the database directly to check if a notification is due and goes to sleep without loading CPU resources.
250
+
251
+ ---
252
+
253
+ ## 🀝 Contributing
254
+
255
+ Contributions are welcome to make Drunken Chunk even better! If you have suggestions or bug fixes:
256
+ 1. Fork this repository.
257
+ 2. Create your feature branch (`git checkout -b feature/amazing-feature`).
258
+ 3. Commit your changes (`git commit -m 'Add some amazing feature'`).
259
+ 4. Push to the branch (`git push origin feature/amazing-feature`).
260
+ 5. Open a Pull Request.
261
+
262
+ ---
263
+
264
+ ## πŸ“„ License
265
+
266
+ Distributed under the MIT License. See [LICENSE](LICENSE) for more information.
package/bin/index.js ADDED
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { spawn, spawnSync } = require('child_process');
4
+ const path = require('path');
5
+
6
+ function getPythonCommand() {
7
+ const cmds = process.platform === 'win32' ? ['python', 'py'] : ['python3', 'python'];
8
+ for (const cmd of cmds) {
9
+ try {
10
+ const res = spawnSync(cmd, ['--version'], { stdio: 'ignore' });
11
+ if (res.status === 0) {
12
+ return cmd;
13
+ }
14
+ } catch (e) {
15
+ // Ignore error and try next
16
+ }
17
+ }
18
+ // Fallback to python
19
+ return process.platform === 'win32' ? 'python' : 'python3';
20
+ }
21
+
22
+ const pythonCmd = getPythonCommand();
23
+
24
+ // The module root is one folder up from `bin/index.js`
25
+ const packageRoot = path.dirname(__dirname);
26
+
27
+ // Set up PYTHONPATH environment variable so python can find the `drunken_chunk` module
28
+ const env = { ...process.env };
29
+ env.PYTHONPATH = env.PYTHONPATH
30
+ ? `${packageRoot}${path.delimiter}${env.PYTHONPATH}`
31
+ : packageRoot;
32
+
33
+ // Run the python module
34
+ const args = ['-m', 'drunken_chunk.cli', ...process.argv.slice(2)];
35
+
36
+ const child = spawn(pythonCmd, args, {
37
+ stdio: 'inherit',
38
+ env: env
39
+ });
40
+
41
+ child.on('close', (code) => {
42
+ process.exit(code !== null ? code : 1);
43
+ });
44
+
45
+ child.on('error', (err) => {
46
+ console.error(`Failed to start the drunken-chunk command: ${err.message}`);
47
+ console.error(`Please ensure Python 3.8+ is installed on your system.`);
48
+ process.exit(1);
49
+ });
@@ -0,0 +1,2 @@
1
+ # Drunken Chunk - Water reminder and behavior analytics tool
2
+ __version__ = "1.0.0"
@@ -0,0 +1,172 @@
1
+ from datetime import datetime
2
+ import collections
3
+ import statistics
4
+
5
+ from .db import get_all_logs, get_profile
6
+ from .profile import calculate_recommended_intake
7
+
8
+ def get_day_name(date_str):
9
+ return datetime.strptime(date_str, "%Y-%m-%d").strftime("%A")
10
+
11
+ def analyze_drinking_behavior():
12
+ """
13
+ Analyzes historical drinking behavior.
14
+ Returns a dict with processed analytics metrics or None if not enough data.
15
+ """
16
+ logs = get_all_logs()
17
+ profile = get_profile()
18
+
19
+ if not profile:
20
+ return {"error": "Profile not configured. Please run 'drunken-chunk init'."}
21
+
22
+ if not logs:
23
+ return {"error": "No water logs recorded yet. Start logging with 'drunken-chunk log'!"}
24
+
25
+ target = profile['custom_target'] if profile['custom_target'] else calculate_recommended_intake(profile['weight'], profile['height'])
26
+
27
+ # 1. Basic Stats
28
+ total_consumed = sum(log['amount'] for log in logs)
29
+ total_entries = len(logs)
30
+
31
+ # Group logs by date
32
+ daily_totals = collections.defaultdict(int)
33
+ daily_entries = collections.defaultdict(list)
34
+
35
+ for log in logs:
36
+ # timestamp format: "YYYY-MM-DD HH:MM:SS"
37
+ dt = datetime.strptime(log['timestamp'], "%Y-%m-%d %H:%M:%S")
38
+ date_str = dt.strftime("%Y-%m-%d")
39
+ daily_totals[date_str] += log['amount']
40
+ daily_entries[date_str].append(dt)
41
+
42
+ unique_days_logged = len(daily_totals)
43
+ avg_daily_consumption = total_consumed / unique_days_logged
44
+
45
+ days_met_target = sum(1 for amount in daily_totals.values() if amount >= target)
46
+ target_hit_rate = (days_met_target / unique_days_logged) * 100
47
+
48
+ # 2. Weekday Analysis
49
+ # To get proper averages, count unique weekday occurrences in the logged period
50
+ weekday_occurrences = collections.defaultdict(set)
51
+ weekday_totals = collections.defaultdict(int)
52
+
53
+ for date_str, amount in daily_totals.items():
54
+ day_name = get_day_name(date_str)
55
+ weekday_totals[day_name] += amount
56
+ weekday_occurrences[day_name].add(date_str)
57
+
58
+ weekday_averages = {}
59
+ for day in ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]:
60
+ occurrences = len(weekday_occurrences[day])
61
+ if occurrences > 0:
62
+ weekday_averages[day] = weekday_totals[day] / occurrences
63
+ else:
64
+ weekday_averages[day] = 0.0
65
+
66
+ # 3. Time of Day Analysis
67
+ time_blocks = {
68
+ "Early Morning (5am - 9am)": 0,
69
+ "Late Morning (9am - 12pm)": 0,
70
+ "Afternoon (12pm - 5pm)": 0,
71
+ "Evening (5pm - 9pm)": 0,
72
+ "Night (9pm - 5am)": 0
73
+ }
74
+
75
+ for log in logs:
76
+ dt = datetime.strptime(log['timestamp'], "%Y-%m-%d %H:%M:%S")
77
+ hour = dt.hour
78
+ if 5 <= hour < 9:
79
+ time_blocks["Early Morning (5am - 9am)"] += log['amount']
80
+ elif 9 <= hour < 12:
81
+ time_blocks["Late Morning (9am - 12pm)"] += log['amount']
82
+ elif 12 <= hour < 17:
83
+ time_blocks["Afternoon (12pm - 5pm)"] += log['amount']
84
+ elif 17 <= hour < 21:
85
+ time_blocks["Evening (5pm - 9pm)"] += log['amount']
86
+ else:
87
+ time_blocks["Night (9pm - 5am)"] += log['amount']
88
+
89
+ # Calculate percentage for each time block
90
+ total_time_blocked = sum(time_blocks.values())
91
+ time_block_pct = {}
92
+ for block, val in time_blocks.items():
93
+ time_block_pct[block] = (val / total_time_blocked * 100) if total_time_blocked > 0 else 0.0
94
+
95
+ # 4. Drink Gaps / Frequency Analysis
96
+ all_gaps_mins = []
97
+
98
+ for date_str, dts in daily_entries.items():
99
+ # Sort chronologically for the day
100
+ dts.sort()
101
+ if len(dts) < 2:
102
+ continue
103
+
104
+ for i in range(len(dts) - 1):
105
+ gap = dts[i+1] - dts[i]
106
+ gap_mins = gap.total_seconds() / 60.0
107
+ all_gaps_mins.append(gap_mins)
108
+
109
+ avg_gap_mins = statistics.mean(all_gaps_mins) if all_gaps_mins else 0
110
+ median_gap_mins = statistics.median(all_gaps_mins) if all_gaps_mins else 0
111
+
112
+ # 5. Consistency Index (last 14 days or fewer if newly joined)
113
+ # Target 80% completion of recommended intake
114
+ consistency_days = list(daily_totals.values())[-14:]
115
+ active_days_count = len(consistency_days)
116
+ met_80_percent_count = sum(1 for amount in consistency_days if amount >= (target * 0.8))
117
+ consistency_index = (met_80_percent_count / active_days_count * 100) if active_days_count > 0 else 0.0
118
+
119
+ # 6. Narrative Insights & Suggestions
120
+ suggestions = []
121
+
122
+ if avg_gap_mins > 0:
123
+ gap_h = int(avg_gap_mins // 60)
124
+ gap_m = int(avg_gap_mins % 60)
125
+ suggestions.append(f"β€’ Your average drinking gap is {gap_h}h {gap_m}m.")
126
+
127
+ # Compare with reminder interval
128
+ reminder_interval = profile['reminder_interval']
129
+ if avg_gap_mins > reminder_interval + 15:
130
+ suggestions.append(f" - This is {int(avg_gap_mins - reminder_interval)} mins slower than your reminder setting. Consider checking notifications more closely.")
131
+ elif avg_gap_mins < reminder_interval:
132
+ suggestions.append(f" - Great job! You are drinking water more frequently than the alarm interval.")
133
+ else:
134
+ suggestions.append("β€’ Drink multiple times a day to calculate gap frequency analysis.")
135
+
136
+ # Analyze best and worst days
137
+ valid_weekday_averages = {k: v for k, v in weekday_averages.items() if v > 0}
138
+ if valid_weekday_averages:
139
+ best_day = max(valid_weekday_averages, key=valid_weekday_averages.get)
140
+ worst_day = min(valid_weekday_averages, key=valid_weekday_averages.get)
141
+
142
+ if best_day != worst_day:
143
+ suggestions.append(f"β€’ Hydration Champion Day: {best_day} (avg: {int(weekday_averages[best_day])} ml).")
144
+ suggestions.append(f"β€’ Dehydration Danger Day: {worst_day} (avg: {int(weekday_averages[worst_day])} ml). Watch out on {worst_day}s!")
145
+
146
+ # Analyze time blocks
147
+ if time_block_pct:
148
+ best_block = max(time_block_pct, key=time_block_pct.get)
149
+ suggestions.append(f"β€’ Peak drinking period: {best_block} ({time_block_pct[best_block]:.1f}% of total).")
150
+
151
+ # Check if night drinking is too high (may affect sleep)
152
+ if time_block_pct.get("Night (9pm - 5am)", 0.0) > 30.0:
153
+ suggestions.append(" - Warning: You drink more than 30% of your water at night. This can disrupt your sleep cycle. Try drinking more during the day.")
154
+
155
+ # Check if early morning is too low (important to kickstart metabolism)
156
+ if time_block_pct.get("Early Morning (5am - 9am)", 0.0) < 5.0:
157
+ suggestions.append(" - Suggestion: Start your day with a glass of water immediately after waking to wake up your organs.")
158
+
159
+ return {
160
+ "target": target,
161
+ "total_consumed": total_consumed,
162
+ "total_entries": total_entries,
163
+ "unique_days": unique_days_logged,
164
+ "avg_daily": avg_daily_consumption,
165
+ "hit_rate": target_hit_rate,
166
+ "weekday_averages": weekday_averages,
167
+ "time_block_pct": time_block_pct,
168
+ "avg_gap_mins": avg_gap_mins,
169
+ "median_gap_mins": median_gap_mins,
170
+ "consistency_index": consistency_index,
171
+ "suggestions": suggestions
172
+ }