Skip to content

Commit 7c78856

Browse files
nikolay-eclaude
andcommitted
Comprehensive dashboard and analytics enhancements
✨ Major Features Added: - 🎨 CYBORG dark theme with Dash Bootstrap Components - πŸ“Š Energy expenditure visualization (active vs basal calories) - πŸ’“ Enhanced heart rate charts (resting, max, average HR) - 🎯 HRV status color coding (green/orange/red indicators) - πŸ“ˆ Consistent timeline alignment across all charts πŸ”§ Technical Improvements: - πŸ”— Complete correlation analysis with stress, energy, and steps - πŸ“€ Enhanced CSV export with energy and steps data - βš–οΈ Synchronized chart dimensions and date ranges - 🐳 Docker environment variable defaults - πŸ“š Comprehensive database schema documentation πŸŽͺ UI/UX Enhancements: - πŸŒ™ Global plotly_dark theme for consistency - πŸ“ Uniform chart heights (400px) and margins - πŸ—“οΈ Timeline synchronization regardless of data availability - 🎨 Professional styling with CYBORG theme πŸ€– Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 330ab6b commit 7c78856

File tree

5 files changed

+540
-19
lines changed

5 files changed

+540
-19
lines changed

β€ŽREADME.mdβ€Ž

Lines changed: 272 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,25 @@
11
# Life-as-Code: Personal Health Analytics Portal
22

3-
A private, multi-user, data-driven portal to track and analyze your health, fitness, and performance metrics from services like Garmin and Hevy.
3+
A comprehensive, private, multi-user health analytics platform that aggregates and analyzes your health, fitness, and performance data from multiple sources including Garmin Connect, Hevy, and Apple HealthKit. Transform your health data into actionable insights with advanced analytics, AI-powered daily briefings, and beautiful visualizations.
4+
5+
## 🎯 What Life-as-Code Does
6+
7+
**πŸ”— Data Integration**
8+
- **Garmin Connect**: Sleep, HRV, heart rate, weight, body composition, stress, and activity data
9+
- **Hevy**: Workout sets, exercises, weights, RPE, and training progression
10+
- **Apple HealthKit**: Energy expenditure, steps, HRV, and comprehensive health metrics
11+
- **Manual Import**: Support for CSV/XML data from wearables and health apps
12+
13+
**πŸ“Š Advanced Analytics**
14+
- **Personal Dashboard**: Interactive charts and trends for all health metrics
15+
- **AI Daily Briefings**: LLM-generated insights and recommendations based on your data
16+
- **Correlation Analysis**: Discover relationships between sleep, training, recovery, and performance
17+
- **Health Scoring**: Personalized thresholds and status indicators for key metrics
18+
19+
**πŸ”’ Privacy-First Architecture**
20+
- **Self-Hosted**: Complete control over your sensitive health data
21+
- **Multi-User**: Secure isolation between users with encrypted credential storage
22+
- **No Cloud Dependencies**: Your data never leaves your infrastructure
423

524
## πŸš€ Quick Start with Docker
625

@@ -144,6 +163,258 @@ This project is for personal use. Please respect the terms of service for Garmin
144163

145164
---
146165

166+
# Database Schema
167+
168+
Life-as-Code uses a PostgreSQL database with the following tables:
169+
170+
## User
171+
**Table:** `users`
172+
173+
**Description:** User accounts for multi-user support.
174+
175+
**Columns:**
176+
- `id`: INTEGER (required) πŸ”‘
177+
- `username`: VARCHAR(80) (required) πŸ“‡ ⭐
178+
- `password_hash`: VARCHAR(200) (required)
179+
- `encryption_key_sealed`: TEXT
180+
- `created_at`: DATETIME
181+
182+
**Relationships:**
183+
- `credentials` β†’ UserCredentials
184+
- `settings` β†’ UserSettings
185+
- `sleep_data` β†’ Sleep
186+
- `weight_data` β†’ Weight
187+
- `heart_rate_data` β†’ HeartRate
188+
- `stress_data` β†’ Stress
189+
- `hrv_data` β†’ HRV
190+
- `energy_data` β†’ Energy
191+
- `steps` β†’ Steps
192+
- `workout_sets` β†’ WorkoutSet
193+
- `data_syncs` β†’ DataSync
194+
195+
## UserCredentials
196+
**Table:** `user_credentials`
197+
198+
**Description:** Encrypted user credentials for external APIs.
199+
200+
**Columns:**
201+
- `id`: INTEGER (required) πŸ”‘
202+
- `user_id`: INTEGER (required) πŸ”— ⭐
203+
- `garmin_email`: VARCHAR(200)
204+
- `encrypted_garmin_password`: VARCHAR(500)
205+
- `encrypted_hevy_api_key`: VARCHAR(500)
206+
- `created_at`: DATETIME
207+
- `updated_at`: DATETIME
208+
209+
**Relationships:**
210+
- `user` β†’ User
211+
212+
## UserSettings
213+
**Table:** `user_settings`
214+
215+
**Description:** User-specific settings and thresholds for personalized analysis.
216+
217+
**Columns:**
218+
- `id`: INTEGER (required) πŸ”‘
219+
- `user_id`: INTEGER (required) πŸ”— ⭐
220+
- `hrv_good_threshold`: INTEGER
221+
- `hrv_moderate_threshold`: INTEGER
222+
- `deep_sleep_good_threshold`: INTEGER
223+
- `deep_sleep_moderate_threshold`: INTEGER
224+
- `total_sleep_good_threshold`: FLOAT
225+
- `total_sleep_moderate_threshold`: FLOAT
226+
- `training_high_volume_threshold`: INTEGER
227+
- `created_at`: DATETIME
228+
- `updated_at`: DATETIME
229+
230+
**Relationships:**
231+
- `user` β†’ User
232+
233+
## Sleep
234+
**Table:** `sleep`
235+
236+
**Description:** Sleep data from Garmin Connect.
237+
238+
**Columns:**
239+
- `id`: INTEGER (required) πŸ”‘
240+
- `user_id`: INTEGER (required) πŸ”— πŸ“‡
241+
- `date`: DATE (required) πŸ“‡
242+
- `deep_minutes`: FLOAT
243+
- `light_minutes`: FLOAT
244+
- `rem_minutes`: FLOAT
245+
- `awake_minutes`: FLOAT
246+
- `total_sleep_minutes`: FLOAT
247+
- `sleep_score`: INTEGER
248+
- `created_at`: DATETIME
249+
250+
**Relationships:**
251+
- `user` β†’ User
252+
253+
**Constraints:**
254+
- UniqueConstraint: `_user_sleep_date_uc`
255+
256+
## HRV
257+
**Table:** `hrv`
258+
259+
**Description:** Heart Rate Variability data from Garmin Connect and Apple Watch.
260+
261+
**Columns:**
262+
- `id`: INTEGER (required) πŸ”‘
263+
- `user_id`: INTEGER (required) πŸ”— πŸ“‡
264+
- `date`: DATE (required) πŸ“‡
265+
- `hrv_avg`: FLOAT
266+
- `hrv_status`: VARCHAR(50)
267+
- `created_at`: DATETIME
268+
269+
**Relationships:**
270+
- `user` β†’ User
271+
272+
**Constraints:**
273+
- UniqueConstraint: `_user_hrv_date_uc`
274+
275+
## Weight
276+
**Table:** `weight`
277+
278+
**Description:** Weight and body composition data from Garmin Connect.
279+
280+
**Columns:**
281+
- `id`: INTEGER (required) πŸ”‘
282+
- `user_id`: INTEGER (required) πŸ”— πŸ“‡
283+
- `date`: DATE (required) πŸ“‡
284+
- `weight_kg`: FLOAT
285+
- `bmi`: FLOAT
286+
- `body_fat_pct`: FLOAT
287+
- `muscle_mass_kg`: FLOAT
288+
- `bone_mass_kg`: FLOAT
289+
- `water_pct`: FLOAT
290+
- `created_at`: DATETIME
291+
292+
**Relationships:**
293+
- `user` β†’ User
294+
295+
## HeartRate
296+
**Table:** `heart_rate`
297+
298+
**Description:** Heart rate data from Garmin Connect.
299+
300+
**Columns:**
301+
- `id`: INTEGER (required) πŸ”‘
302+
- `user_id`: INTEGER (required) πŸ”— πŸ“‡
303+
- `date`: DATE (required) πŸ“‡
304+
- `resting_hr`: INTEGER
305+
- `max_hr`: INTEGER
306+
- `avg_hr`: INTEGER
307+
- `created_at`: DATETIME
308+
309+
**Relationships:**
310+
- `user` β†’ User
311+
312+
**Constraints:**
313+
- UniqueConstraint: `_user_heart_rate_date_uc`
314+
315+
## Stress
316+
**Table:** `stress`
317+
318+
**Description:** Daily stress data from Garmin.
319+
320+
**Columns:**
321+
- `id`: INTEGER (required) πŸ”‘
322+
- `user_id`: INTEGER (required) πŸ”— πŸ“‡
323+
- `date`: DATE (required) πŸ“‡
324+
- `avg_stress`: FLOAT
325+
- `max_stress`: FLOAT
326+
- `stress_level`: VARCHAR(20)
327+
- `rest_stress`: FLOAT
328+
- `activity_stress`: FLOAT
329+
- `created_at`: DATETIME
330+
331+
**Relationships:**
332+
- `user` β†’ User
333+
334+
**Constraints:**
335+
- UniqueConstraint: `unique_user_stress_date`
336+
337+
## Energy
338+
**Table:** `energy`
339+
340+
**Description:** Daily energy expenditure data from Apple Watch and Garmin.
341+
342+
**Columns:**
343+
- `id`: INTEGER (required) πŸ”‘
344+
- `user_id`: INTEGER (required) πŸ”— πŸ“‡
345+
- `date`: DATE (required) πŸ“‡
346+
- `active_energy`: FLOAT
347+
- `basal_energy`: FLOAT
348+
- `created_at`: DATETIME
349+
350+
**Relationships:**
351+
- `user` β†’ User
352+
353+
**Constraints:**
354+
- UniqueConstraint: `_user_date_energy_uc`
355+
356+
## Steps
357+
**Table:** `steps`
358+
359+
**Description:** Daily steps and distance data from Garmin Connect.
360+
361+
**Columns:**
362+
- `id`: INTEGER (required) πŸ”‘
363+
- `user_id`: INTEGER (required) πŸ”— πŸ“‡
364+
- `date`: DATE (required) πŸ“‡
365+
- `total_steps`: INTEGER
366+
- `total_distance`: FLOAT
367+
- `step_goal`: INTEGER
368+
- `created_at`: DATETIME
369+
370+
**Relationships:**
371+
- `user` β†’ User
372+
373+
**Constraints:**
374+
- UniqueConstraint: `_user_date_steps_uc`
375+
376+
## WorkoutSet
377+
**Table:** `workout_sets`
378+
379+
**Description:** Individual workout sets from Hevy.
380+
381+
**Columns:**
382+
- `id`: INTEGER (required) πŸ”‘
383+
- `user_id`: INTEGER (required) πŸ”— πŸ“‡
384+
- `date`: DATE (required) πŸ“‡
385+
- `exercise`: VARCHAR(200) (required)
386+
- `weight_kg`: FLOAT
387+
- `reps`: INTEGER
388+
- `rpe`: FLOAT
389+
- `set_type`: VARCHAR(50)
390+
- `duration_seconds`: INTEGER
391+
- `distance_meters`: FLOAT
392+
- `created_at`: DATETIME
393+
394+
**Relationships:**
395+
- `user` β†’ User
396+
397+
## DataSync
398+
**Table:** `data_sync`
399+
400+
**Description:** Track when data was last synced from external sources.
401+
402+
**Columns:**
403+
- `id`: INTEGER (required) πŸ”‘
404+
- `user_id`: INTEGER (required) πŸ”— πŸ“‡
405+
- `source`: VARCHAR(50) (required)
406+
- `data_type`: VARCHAR(50) (required)
407+
- `last_sync_date`: DATE
408+
- `last_sync_timestamp`: DATETIME
409+
- `records_synced`: INTEGER
410+
- `status`: VARCHAR(20)
411+
- `error_message`: TEXT
412+
413+
**Relationships:**
414+
- `user` β†’ User
415+
416+
---
417+
147418
**Built with**: Python, Flask, Dash, PostgreSQL, Docker
148419
**Supported APIs**: Garmin Connect, Hevy
149420
**Architecture**: Multi-user, containerized, security-first

β€Žanalyze_correlations.pyβ€Ž

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
from sqlalchemy import select
1515

1616
from database import SessionLocal
17-
from models import HRV, HeartRate, Sleep, Steps, Stress, Weight, WorkoutSet
17+
from models import HRV, Energy, HeartRate, Sleep, Steps, Stress, Weight, WorkoutSet
1818

1919

2020
def load_data_from_database(user_id: int, days=90):
@@ -36,6 +36,7 @@ def load_data_from_database(user_id: int, days=90):
3636
(Weight, "weight"),
3737
(HeartRate, "heart_rate"),
3838
(Stress, "stress"),
39+
(Energy, "energy"),
3940
(Steps, "steps"),
4041
(WorkoutSet, "workouts"),
4142
]
@@ -96,6 +97,9 @@ def merge_datasets_by_date(datasets):
9697
("hrv", ["hrv_avg"]),
9798
("weight", ["weight_kg", "bmi", "body_fat_pct"]),
9899
("heart_rate", ["resting_hr", "max_hr", "avg_hr"]),
100+
("stress", ["avg_stress", "max_stress"]),
101+
("energy", ["active_energy", "basal_energy"]),
102+
("steps", ["total_steps", "total_distance"]),
99103
]
100104

101105
for key, columns in dataset_configs:
@@ -182,6 +186,38 @@ def analyze_key_correlations_from_db(user_id: int):
182186
"y_label": "HRV Average (ms)",
183187
"question": "How does resting heart rate relate to HRV?",
184188
},
189+
{
190+
"x": "avg_stress",
191+
"y": "sleep_score",
192+
"title": "😰 Stress vs Sleep Quality",
193+
"x_label": "Average Stress Level",
194+
"y_label": "Sleep Score",
195+
"question": "Does stress affect sleep quality?",
196+
},
197+
{
198+
"x": "avg_stress",
199+
"y": "hrv_avg",
200+
"title": "😰 Stress vs HRV Recovery",
201+
"x_label": "Average Stress Level",
202+
"y_label": "HRV Average (ms)",
203+
"question": "How does stress impact recovery (HRV)?",
204+
},
205+
{
206+
"x": "active_energy",
207+
"y": "total_sleep_minutes",
208+
"title": "πŸ”₯ Active Energy vs Sleep Duration",
209+
"x_label": "Active Energy (kcal)",
210+
"y_label": "Total Sleep (minutes)",
211+
"question": "Does higher activity lead to more sleep?",
212+
},
213+
{
214+
"x": "total_steps",
215+
"y": "active_energy",
216+
"title": "🚢 Steps vs Active Energy",
217+
"x_label": "Daily Steps",
218+
"y_label": "Active Energy (kcal)",
219+
"question": "How do steps correlate with energy expenditure?",
220+
},
185221
]
186222

187223
# Generate correlation plots
@@ -260,6 +296,9 @@ def generate_correlation_report(user_id: int):
260296
"total_sleep_minutes",
261297
"resting_hr",
262298
"volume",
299+
"avg_stress",
300+
"active_energy",
301+
"total_steps",
263302
]
264303
available_metrics = [
265304
m for m in key_metrics if m in merged_df.columns and merged_df[m].sum() > 0

0 commit comments

Comments
Β (0)