Severian commited on
Commit
e04b1f9
·
verified ·
1 Parent(s): d470cfc

Upload 3 files

Browse files
Files changed (4) hide show
  1. .gitattributes +1 -0
  2. app.py +1936 -0
  3. logo.png +3 -0
  4. requirements.txt +4 -0
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ logo.png filter=lfs diff=lfs merge=lfs -text
app.py ADDED
@@ -0,0 +1,1936 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import datetime
3
+ import json
4
+ import os
5
+ import calendar
6
+ from pathlib import Path
7
+ import plotly.graph_objects as go
8
+ import plotly.express as px
9
+ import numpy as np
10
+
11
+ # Create data directory if it doesn't exist
12
+ DATA_DIR = Path("daily_data")
13
+ try:
14
+ DATA_DIR.mkdir(parents=True, exist_ok=True)
15
+ except Exception as e:
16
+ print(f"Error creating data directory: {e}")
17
+
18
+ JOURNAL_QUESTIONS = [
19
+ "What made you happiest today, and why?",
20
+ "What was the biggest challenge you faced today, and how did you approach it?",
21
+ "What's one thing you learned or realized today?",
22
+ "What are you grateful for today?",
23
+ "What emotion did you feel the most today? What triggered it?",
24
+ "What's one moment or interaction that stood out to you?",
25
+ "What progress did you make toward your goals today?",
26
+ "Did you notice any recurring habits or patterns in your day?",
27
+ "How did you support or connect with someone today?",
28
+ "What could you have done differently to improve your day?",
29
+ "What's one thing you want to prioritize or focus on tomorrow?",
30
+ "If today were a chapter in your life story, what would its title be?"
31
+ ]
32
+
33
+ def get_date_key():
34
+ """Get current date in YYYY-MM-DD format"""
35
+ return datetime.datetime.now().strftime("%Y-%m-%d")
36
+
37
+ def save_daily_data(data, date_key=None):
38
+ """Save daily data to JSON file"""
39
+ if date_key is None:
40
+ date_key = get_date_key()
41
+
42
+ # Ensure data is a dictionary
43
+ if not isinstance(data, dict):
44
+ data = create_empty_daily_data()
45
+
46
+ # Convert DataFrame to list for JSON serialization
47
+ if "focus" in data:
48
+ if "priorities" in data["focus"]:
49
+ priorities = data["focus"]["priorities"]
50
+ if hasattr(priorities, '__array__') or hasattr(priorities, 'values'):
51
+ data["focus"]["priorities"] = priorities.values.tolist() if hasattr(priorities, 'values') else priorities.tolist()
52
+ if "later" in data["focus"]:
53
+ later = data["focus"]["later"]
54
+ if hasattr(later, '__array__') or hasattr(later, 'values'):
55
+ data["focus"]["later"] = later.values.tolist() if hasattr(later, 'values') else later.tolist()
56
+
57
+ # Deep merge required fields with existing data
58
+ required_fields = {
59
+ "tracking_metrics": {
60
+ "productivity": 0,
61
+ "energy": 0,
62
+ "mood": 0,
63
+ "sleep_quality": 0,
64
+ "exercise_intensity": 0
65
+ },
66
+ "habits": {},
67
+ "focus": {
68
+ "priorities": [],
69
+ "later": [],
70
+ "priority_reward": "",
71
+ "later_reward": ""
72
+ },
73
+ "meals": {
74
+ "breakfast": "",
75
+ "lunch": "",
76
+ "dinner": "",
77
+ "snacks": ""
78
+ },
79
+ "last_updated": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
80
+ }
81
+
82
+ for key, default_value in required_fields.items():
83
+ if key not in data:
84
+ data[key] = default_value
85
+ elif isinstance(default_value, dict):
86
+ for subkey, subvalue in default_value.items():
87
+ if subkey not in data[key]:
88
+ data[key][subkey] = subvalue
89
+
90
+ file_path = DATA_DIR / f"{date_key}.json"
91
+
92
+ try:
93
+ # Ensure the directory exists again (in case it was deleted)
94
+ DATA_DIR.mkdir(parents=True, exist_ok=True)
95
+
96
+ # Write directly to the file first
97
+ with open(file_path, 'w') as f:
98
+ json.dump(data, f, indent=2, sort_keys=True)
99
+
100
+ except Exception as e:
101
+ print(f"Error saving data: {e}")
102
+ try:
103
+ # Fallback: try to save in the current directory
104
+ with open(f"{date_key}.json", 'w') as f:
105
+ json.dump(data, f, indent=2, sort_keys=True)
106
+ print(f"Saved data to current directory as fallback")
107
+ except Exception as e2:
108
+ print(f"Final save attempt failed: {e2}")
109
+
110
+ def clean_daily_data_file(date_key):
111
+ """Clean up corrupted JSON file and restore it with valid data"""
112
+ file_path = DATA_DIR / f"{date_key}.json"
113
+ try:
114
+ if file_path.exists():
115
+ with open(file_path, 'r') as f:
116
+ content = f.read().strip()
117
+
118
+ try:
119
+ # Try to parse the content directly first
120
+ data = json.loads(content)
121
+ return validate_data_structure(data)
122
+ except json.JSONDecodeError:
123
+ # If that fails, try to clean the content
124
+ content = content.rstrip(', \t\n\r')
125
+ last_brace = content.rfind('}')
126
+ if last_brace != -1:
127
+ content = content[:last_brace + 1]
128
+ try:
129
+ data = json.loads(content)
130
+ data = validate_data_structure(data)
131
+ # Save the cleaned data
132
+ save_daily_data(data, date_key)
133
+ return data
134
+ except:
135
+ pass
136
+
137
+ # If all cleaning attempts fail, create new data
138
+ print(f"Creating new data for {date_key}")
139
+ data = create_empty_daily_data()
140
+ save_daily_data(data, date_key)
141
+ return data
142
+
143
+ except Exception as e:
144
+ print(f"Error cleaning data file {date_key}: {e}")
145
+ return create_empty_daily_data()
146
+
147
+ def load_daily_data(date_key=None):
148
+ """Load daily data from JSON file"""
149
+ if date_key is None:
150
+ date_key = get_date_key()
151
+
152
+ file_path = DATA_DIR / f"{date_key}.json"
153
+ try:
154
+ if file_path.exists():
155
+ try:
156
+ with open(file_path, 'r') as f:
157
+ data = json.load(f)
158
+ try:
159
+ # Validate and update existing data
160
+ data = validate_data_structure(data)
161
+
162
+ # Only save if the file already existed and needed updates
163
+ save_daily_data(data, date_key)
164
+
165
+ return data
166
+ except Exception as e:
167
+ print(f"Error validating data: {e}")
168
+ return create_empty_daily_data()
169
+ except json.JSONDecodeError as e:
170
+ print(f"Error loading data: {e}")
171
+ # Try to clean up the corrupted file
172
+ data = clean_daily_data_file(date_key)
173
+ return data
174
+ else:
175
+ # Only create new file if it's today's date
176
+ if date_key == get_date_key():
177
+ data = create_empty_daily_data()
178
+ save_daily_data(data, date_key)
179
+ return data
180
+ else:
181
+ # Return empty metrics for historical dates that don't exist
182
+ return {
183
+ "tracking_metrics": {
184
+ "productivity": 0,
185
+ "energy": 0,
186
+ "mood": 0,
187
+ "sleep_quality": 0,
188
+ "exercise_intensity": 0
189
+ }
190
+ }
191
+ except Exception as e:
192
+ print(f"Error loading data: {e}")
193
+ return create_empty_daily_data()
194
+
195
+ def validate_data_structure(data):
196
+ """Validate and update data structure if needed"""
197
+ if not isinstance(data, dict):
198
+ return create_empty_daily_data()
199
+
200
+ # Create a new data structure with all required fields
201
+ valid_data = create_empty_daily_data()
202
+
203
+ # Copy existing data while ensuring all required fields exist
204
+ for key, default_value in valid_data.items():
205
+ if key in data:
206
+ if isinstance(default_value, dict):
207
+ valid_data[key] = {**default_value, **data[key]}
208
+ else:
209
+ valid_data[key] = data[key]
210
+
211
+ return valid_data
212
+
213
+ def create_empty_daily_data():
214
+ """Create empty data structure for a new day"""
215
+ # Load current habits list
216
+ current_habits = load_habits()
217
+
218
+ data = {
219
+ "habits": {habit: [False] * 7 for habit in current_habits},
220
+ "focus": {
221
+ "priorities": [
222
+ ["Set Up Onboarding", "In Progress"],
223
+ ["Finish Plotnaut", "Not Started"],
224
+ ["Be.Ta Labs social media posts", "Not Started"]
225
+ ],
226
+ "later": [
227
+ ["Text to vid nature documentary", "Not Started"],
228
+ ["Quick AI vids", "Not Started"],
229
+ ["PEER (MoE LoRa experiment)", "Not Started"]
230
+ ],
231
+ "priority_reward": "",
232
+ "later_reward": ""
233
+ },
234
+ "meals": {
235
+ "breakfast": "",
236
+ "lunch": "",
237
+ "dinner": "",
238
+ "snacks": ""
239
+ },
240
+ "last_updated": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
241
+ "tracking_metrics": {
242
+ "productivity": 0,
243
+ "energy": 0,
244
+ "mood": 0,
245
+ "sleep_quality": 0,
246
+ "exercise_intensity": 0
247
+ }
248
+ }
249
+
250
+ # Add journal section
251
+ data["journal"] = {
252
+ question: "" for question in JOURNAL_QUESTIONS
253
+ }
254
+
255
+ return data
256
+
257
+ def get_week_dates():
258
+ """Get dates for current week"""
259
+ today = datetime.date.today()
260
+ monday = today - datetime.timedelta(days=today.weekday())
261
+ return [(monday + datetime.timedelta(days=i)).strftime("%Y-%m-%d") for i in range(7)]
262
+
263
+ def get_month_dates():
264
+ """Get dates for current month"""
265
+ today = datetime.date.today()
266
+ _, num_days = calendar.monthrange(today.year, today.month)
267
+ return [(datetime.date(today.year, today.month, day)).strftime("%Y-%m-%d")
268
+ for day in range(1, num_days + 1)]
269
+
270
+ def load_week_data():
271
+ """Load data for the current week"""
272
+ week_data = {}
273
+ week_dates = get_week_dates()
274
+
275
+ for date in week_dates:
276
+ file_path = DATA_DIR / f"{date}.json"
277
+ if file_path.exists():
278
+ try:
279
+ data = load_daily_data(date)
280
+ week_data[date] = data
281
+ except Exception as e:
282
+ print(f"Error loading week data for {date}: {e}")
283
+ week_data[date] = create_empty_daily_data()
284
+ else:
285
+ # Don't create a file, just return empty data structure
286
+ week_data[date] = {
287
+ "tracking_metrics": {
288
+ "productivity": 0,
289
+ "energy": 0,
290
+ "mood": 0,
291
+ "sleep_quality": 0,
292
+ "exercise_intensity": 0
293
+ }
294
+ }
295
+ return week_data
296
+
297
+ def load_month_data():
298
+ """Load data for the current month"""
299
+ month_data = {}
300
+ month_dates = get_month_dates()
301
+
302
+ for date in month_dates:
303
+ file_path = DATA_DIR / f"{date}.json"
304
+ if file_path.exists():
305
+ try:
306
+ data = load_daily_data(date)
307
+ month_data[date] = data
308
+ except Exception as e:
309
+ print(f"Error loading month data for {date}: {e}")
310
+ month_data[date] = create_empty_daily_data()
311
+ else:
312
+ # Don't create a file, just return empty data structure
313
+ month_data[date] = {
314
+ "tracking_metrics": {
315
+ "productivity": 0,
316
+ "energy": 0,
317
+ "mood": 0,
318
+ "sleep_quality": 0,
319
+ "exercise_intensity": 0
320
+ }
321
+ }
322
+ return month_data
323
+
324
+ def calculate_success_rate(data_dict, metric):
325
+ """Calculate success rate for a given metric"""
326
+ if not data_dict:
327
+ return 0
328
+ total = 0
329
+ count = 0
330
+ for date, data in data_dict.items():
331
+ # Ensure tracking_metrics exists for each data point
332
+ if "tracking_metrics" not in data:
333
+ data["tracking_metrics"] = {
334
+ "productivity": 0,
335
+ "energy": 0,
336
+ "mood": 0,
337
+ "sleep_quality": 0,
338
+ "exercise_intensity": 0
339
+ }
340
+ if metric in data.get("tracking_metrics", {}):
341
+ total += data["tracking_metrics"][metric]
342
+ count += 1
343
+ return round((total / count) if count > 0 else 0, 2)
344
+
345
+ # Replace the hardcoded HABITS list with a function to load habits
346
+ def get_default_habits():
347
+ """Get the default habits list if no custom list exists"""
348
+ return [
349
+ # Morning Routine (Essential)
350
+ "Wake up & Mindfulness (30min)",
351
+ "Morning Exercise/Yoga",
352
+ "Meditation (20min)",
353
+ "Brain Training GPT (20min)",
354
+ "Walk with Nala",
355
+
356
+ # After Work Activities
357
+ "Evening Stretch (20min)",
358
+ "Massage (20min)",
359
+ "Active Learning (20min)",
360
+ "Journal",
361
+ "Guitar Practice",
362
+
363
+ # Flexible Throughout Day
364
+ "Reading (20min)",
365
+ "Exercise KB",
366
+ "Body Tapping",
367
+ "Take a photo",
368
+ "Bike Ride",
369
+ "Future Planning"
370
+ ]
371
+
372
+ def load_habits():
373
+ """Load the current habits list from the configuration"""
374
+ config_path = Path("habits_config.json")
375
+ if config_path.exists():
376
+ try:
377
+ with open(config_path, 'r') as f:
378
+ data = json.load(f)
379
+ return data.get("habits", get_default_habits())
380
+ except Exception as e:
381
+ print(f"Error loading habits: {e}")
382
+ return get_default_habits()
383
+
384
+ def save_habits(habits_list):
385
+ """Save the current habits list to configuration"""
386
+ config_path = Path("habits_config.json")
387
+ try:
388
+ # Ensure the list is unique and sorted
389
+ habits_list = sorted(list(set(habits_list)))
390
+
391
+ # Save to config file
392
+ with open(config_path, 'w') as f:
393
+ json.dump({"habits": habits_list}, f, indent=2)
394
+
395
+ # Also update the current day's data to match
396
+ current_data = load_daily_data()
397
+ new_habits = {habit: [False] * 7 for habit in habits_list}
398
+
399
+ # Preserve existing habit states
400
+ for habit, states in current_data["habits"].items():
401
+ if habit in new_habits:
402
+ new_habits[habit] = states
403
+
404
+ current_data["habits"] = new_habits
405
+ save_daily_data(current_data)
406
+
407
+ except Exception as e:
408
+ print(f"Error saving habits: {e}")
409
+
410
+ # Remove redundant constants
411
+ DEFAULT_SELF_CARE_ITEMS = [
412
+ "Nala", "Yoga (AM)", "Vitamins", "Outside",
413
+ "Stretch (PM)", "Reading", "Learning", "Journal",
414
+ "Tapping", "Meditate", "Brain Train"
415
+ ]
416
+
417
+ def parse_time(time_str):
418
+ """Parse time string in either 12-hour or 24-hour format"""
419
+ try:
420
+ # Try 24-hour format first
421
+ return datetime.datetime.strptime(time_str, "%H:%M").strftime("%H:%M")
422
+ except ValueError:
423
+ try:
424
+ # Try 12-hour format with AM/PM
425
+ return datetime.datetime.strptime(time_str.strip().upper(), "%I:%M %p").strftime("%H:%M")
426
+ except ValueError:
427
+ try:
428
+ # Try 12-hour format without space before AM/PM
429
+ return datetime.datetime.strptime(time_str.strip().upper(), "%I:%M%p").strftime("%H:%M")
430
+ except ValueError:
431
+ return "00:00" # Default if parsing fails
432
+
433
+ def calculate_age_stats(birth_date_str, birth_time_str="00:00"):
434
+ """Calculate age-related statistics with precise birth date and time"""
435
+ try:
436
+ # Parse birth date and time
437
+ try:
438
+ birth_time = parse_time(birth_time_str)
439
+ except ValueError:
440
+ return {"Error": "Invalid time format. Please use either HH:MM (24-hour) or HH:MM AM/PM (12-hour)"}
441
+
442
+ birth_datetime = datetime.datetime.strptime(f"{birth_date_str} {birth_time}", "%Y-%m-%d %H:%M")
443
+ now = datetime.datetime.now()
444
+
445
+ # Calculate time differences
446
+ time_lived = now - birth_datetime
447
+ age_days = time_lived.days
448
+ age_hours = time_lived.total_seconds() / 3600
449
+ age_minutes = time_lived.total_seconds() / 60
450
+ age_weeks = age_days / 7
451
+ age_months = age_days / 30.44
452
+ age_years = age_days / 365.25
453
+
454
+ # Calculate time until milestones
455
+ days_until_90 = (datetime.datetime(birth_datetime.year + 90, birth_datetime.month, birth_datetime.day) - now).days
456
+ weeks_until_90 = days_until_90 / 7
457
+ months_until_90 = days_until_90 / 30.44
458
+ years_until_90 = days_until_90 / 365.25
459
+
460
+ # Calculate percentages and milestones
461
+ percent_lived = (age_years / 90) * 100
462
+ percent_of_year = (now.timetuple().tm_yday / 365.25) * 100
463
+ percent_of_month = (now.day / calendar.monthrange(now.year, now.month)[1]) * 100
464
+ percent_of_week = ((now.weekday() + 1) / 7) * 100
465
+ percent_of_day = ((now.hour * 3600 + now.minute * 60 + now.second) / 86400) * 100
466
+
467
+ # Calculate next birthday
468
+ next_birthday = datetime.datetime(now.year, birth_datetime.month, birth_datetime.day)
469
+ if next_birthday < now:
470
+ next_birthday = datetime.datetime(now.year + 1, birth_datetime.month, birth_datetime.day)
471
+ days_to_birthday = (next_birthday - now).days
472
+
473
+ return {
474
+ "Current Age": {
475
+ "Years": f"{age_years:.2f}",
476
+ "Months": f"{age_months:.1f}",
477
+ "Weeks": f"{age_weeks:.1f}",
478
+ "Days": str(age_days),
479
+ "Hours": f"{age_hours:,.0f}",
480
+ "Minutes": f"{age_minutes:,.0f}"
481
+ },
482
+ "Time Until 90": {
483
+ "Years": f"{years_until_90:.1f}",
484
+ "Months": f"{months_until_90:.1f}",
485
+ "Weeks": f"{weeks_until_90:.1f}",
486
+ "Days": str(days_until_90)
487
+ },
488
+ "Life Progress": {
489
+ "Life Percentage": f"{percent_lived:.1f}%",
490
+ "Current Year": f"{percent_of_year:.1f}%",
491
+ "Current Month": f"{percent_of_month:.1f}%",
492
+ "Current Week": f"{percent_of_week:.1f}%",
493
+ "Current Day": f"{percent_of_day:.1f}%"
494
+ },
495
+ "Milestones": {
496
+ "Days to Next Birthday": str(days_to_birthday),
497
+ "Weeks Lived": f"{age_weeks:,.0f}",
498
+ "Days Lived": f"{age_days:,d}",
499
+ "Hours Lived": f"{age_hours:,.0f}",
500
+ "10,000 Days": f"{10000 - (age_days % 10000)} days until next 10k milestone",
501
+ "Life Seasons": f"You are in season {int(age_years/20) + 1} of your life"
502
+ }
503
+ }
504
+ except ValueError as e:
505
+ return {"Error": f"Invalid date or time format. Please use YYYY-MM-DD for date and HH:MM (24-hour) or HH:MM AM/PM (12-hour) for time."}
506
+
507
+ def update_progress(checkboxes):
508
+ """Calculate progress percentage"""
509
+ completed = sum(1 for x in checkboxes if x)
510
+ total = len(checkboxes)
511
+ return f"{(completed/total)*100:.1f}%" if total > 0 else "0%"
512
+
513
+ def check_and_refresh_day(current_data):
514
+ """Check if we need to start a new day"""
515
+ # Ensure tracking_metrics exists
516
+ if "tracking_metrics" not in current_data:
517
+ current_data["tracking_metrics"] = {
518
+ "productivity": 0,
519
+ "energy": 0,
520
+ "mood": 0,
521
+ "sleep_quality": 0,
522
+ "exercise_intensity": 0
523
+ }
524
+
525
+ last_updated = datetime.datetime.strptime(current_data["last_updated"], "%Y-%m-%d %H:%M:%S")
526
+ current_time = datetime.datetime.now()
527
+
528
+ # If it's a new day (past midnight)
529
+ if last_updated.date() < current_time.date():
530
+ # Save current data with previous date
531
+ previous_date = last_updated.strftime("%Y-%m-%d")
532
+ save_daily_data(current_data, previous_date)
533
+
534
+ # Create new empty data for today
535
+ current_data = create_empty_daily_data()
536
+ save_daily_data(current_data)
537
+
538
+ # Always return the current data
539
+ return current_data
540
+
541
+ def update_data(data_key, value):
542
+ """Update specific data in the daily record"""
543
+ current_data = load_daily_data()
544
+ current_data = check_and_refresh_day(current_data)
545
+
546
+ # Convert value to list only if it's a DataFrame or numpy array and not a dict
547
+ if not isinstance(value, dict) and (hasattr(value, '__array__') or hasattr(value, 'values')):
548
+ value = value.values.tolist() if hasattr(value, 'values') else value.tolist()
549
+
550
+ # Update the specified data
551
+ keys = data_key.split('.')
552
+ target = current_data
553
+
554
+ # Special handling for habits which are stored as lists
555
+ if keys[0] == "habits" and len(keys) == 3:
556
+ habit_name = keys[1]
557
+ day_index = int(keys[2]) # Convert string index to integer
558
+ if habit_name in target["habits"]:
559
+ target["habits"][habit_name][day_index] = value
560
+ else:
561
+ # Normal dictionary path traversal
562
+ for key in keys[:-1]:
563
+ target = target[key]
564
+ target[keys[-1]] = value
565
+
566
+ current_data["last_updated"] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
567
+ save_daily_data(current_data)
568
+ return current_data
569
+
570
+ def update_activity(new_name, old_name):
571
+ """Update a self-care activity name and maintain its status"""
572
+ if not new_name.strip() or new_name == old_name:
573
+ return gr.update()
574
+
575
+ try:
576
+ current_data = load_daily_data()
577
+
578
+ # Ensure proper data structure
579
+ if "self_care" not in current_data or not isinstance(current_data["self_care"], dict):
580
+ current_data["self_care"] = {
581
+ "items": DEFAULT_SELF_CARE_ITEMS.copy(),
582
+ "status": {item: "Not Done" for item in DEFAULT_SELF_CARE_ITEMS}
583
+ }
584
+ if "items" not in current_data["self_care"]:
585
+ current_data["self_care"]["items"] = list(current_data["self_care"].keys())
586
+ current_data["self_care"]["status"] = current_data["self_care"].copy()
587
+
588
+ # Update activity in items list
589
+ if old_name in current_data["self_care"]["items"]:
590
+ idx = current_data["self_care"]["items"].index(old_name)
591
+ current_data["self_care"]["items"][idx] = new_name.strip()
592
+
593
+ # Update status
594
+ if "status" in current_data["self_care"]:
595
+ if old_name in current_data["self_care"]["status"]:
596
+ current_data["self_care"]["status"][new_name.strip()] = current_data["self_care"]["status"].pop(old_name)
597
+
598
+ save_daily_data(current_data)
599
+ return gr.update()
600
+ except Exception as e:
601
+ print(f"Error updating activity: {e}")
602
+ return gr.update()
603
+
604
+ def create_life_visualizations(stats):
605
+ figures = {}
606
+
607
+ # 1. Life Progress Donut Chart
608
+ progress_data = {k: float(v.strip('%')) for k, v in stats["Life Progress"].items()}
609
+ fig_progress = go.Figure(data=[go.Pie(
610
+ labels=list(progress_data.keys()),
611
+ values=list(progress_data.values()),
612
+ hole=.3,
613
+ marker_colors=px.colors.qualitative.Set3
614
+ )])
615
+ fig_progress.update_layout(title="Life Progress Metrics")
616
+ figures["progress"] = fig_progress
617
+
618
+ # 2. Time Remaining Bar Chart
619
+ time_until = {k: float(v) for k, v in stats["Time Until 90"].items()}
620
+ fig_remaining = go.Figure(data=[go.Bar(
621
+ x=list(time_until.keys()),
622
+ y=list(time_until.values()),
623
+ marker_color='lightblue'
624
+ )])
625
+ fig_remaining.update_layout(title="Time Until 90")
626
+ figures["remaining"] = fig_remaining
627
+
628
+ # 3. Life Timeline
629
+ current_year = float(stats["Current Age"]["Years"])
630
+ fig_timeline = go.Figure()
631
+
632
+ # Add seasons
633
+ seasons = [
634
+ {"name": "Learning & Discovery", "start": 0, "end": 20, "color": "lightgreen"},
635
+ {"name": "Creation & Building", "start": 20, "end": 40, "color": "lightblue"},
636
+ {"name": "Mastery & Leadership", "start": 40, "end": 60, "color": "lightyellow"},
637
+ {"name": "Wisdom & Guidance", "start": 60, "end": 80, "color": "lightpink"},
638
+ {"name": "Legacy & Reflection", "start": 80, "end": 90, "color": "lavender"}
639
+ ]
640
+
641
+ for season in seasons:
642
+ fig_timeline.add_shape(
643
+ type="rect",
644
+ x0=season["start"],
645
+ x1=season["end"],
646
+ y0=0,
647
+ y1=1,
648
+ fillcolor=season["color"],
649
+ opacity=0.3,
650
+ layer="below",
651
+ line_width=0,
652
+ )
653
+
654
+ # Add current age marker
655
+ fig_timeline.add_trace(go.Scatter(
656
+ x=[current_year, current_year],
657
+ y=[0, 1],
658
+ mode="lines",
659
+ line=dict(color="red", width=2),
660
+ name="Current Age"
661
+ ))
662
+
663
+ fig_timeline.update_layout(
664
+ title="Life Seasons Timeline",
665
+ xaxis_title="Age",
666
+ yaxis_showticklabels=False,
667
+ showlegend=True
668
+ )
669
+ figures["timeline"] = fig_timeline
670
+
671
+ return figures
672
+
673
+ def update_metrics(prod, en, m, slp, ex, selected_date=None):
674
+ """Update tracking metrics and return updated weekly grid"""
675
+ if selected_date is None:
676
+ selected_date = get_date_key()
677
+
678
+ # Load data for the selected date
679
+ current_data = load_daily_data(selected_date)
680
+
681
+ # Update the metrics
682
+ current_data["tracking_metrics"] = {
683
+ "productivity": prod,
684
+ "energy": en,
685
+ "mood": m,
686
+ "sleep_quality": slp,
687
+ "exercise_intensity": ex
688
+ }
689
+
690
+ # Save the updated data
691
+ save_daily_data(current_data, selected_date)
692
+
693
+ # Reload week data to get updated grid
694
+ week_data = load_week_data()
695
+
696
+ # Create updated weekly grid values
697
+ week_dates = get_week_dates()
698
+ grid_values = [
699
+ ["Productivity"] + [week_data[date]["tracking_metrics"]["productivity"] for date in week_dates],
700
+ ["Energy"] + [week_data[date]["tracking_metrics"]["energy"] for date in week_dates],
701
+ ["Mood"] + [week_data[date]["tracking_metrics"]["mood"] for date in week_dates],
702
+ ["Sleep"] + [week_data[date]["tracking_metrics"]["sleep_quality"] for date in week_dates],
703
+ ["Exercise"] + [week_data[date]["tracking_metrics"]["exercise_intensity"] for date in week_dates]
704
+ ]
705
+
706
+ # Calculate monthly averages
707
+ month_data = load_month_data()
708
+ avg_prod = calculate_success_rate(month_data, "productivity")
709
+ avg_energy = calculate_success_rate(month_data, "energy")
710
+ avg_mood = calculate_success_rate(month_data, "mood")
711
+ avg_sleep = calculate_success_rate(month_data, "sleep_quality")
712
+ avg_exercise = calculate_success_rate(month_data, "exercise_intensity")
713
+
714
+ return [
715
+ grid_values, # Weekly grid values
716
+ f"Average Productivity: {avg_prod}/5",
717
+ f"Average Energy: {avg_energy}/5",
718
+ f"Average Mood: {avg_mood}/5",
719
+ f"Average Sleep: {avg_sleep}/5",
720
+ f"Average Exercise: {avg_exercise}/5"
721
+ ]
722
+
723
+ def update_journal_entry(question, answer):
724
+ """Update a specific journal entry"""
725
+ current_data = load_daily_data()
726
+
727
+ # Ensure journal section exists
728
+ if "journal" not in current_data:
729
+ current_data["journal"] = {q: "" for q in JOURNAL_QUESTIONS}
730
+
731
+ # Update the specific question's answer
732
+ current_data["journal"][question] = answer
733
+
734
+ # Save the updated data
735
+ save_daily_data(current_data)
736
+ return current_data
737
+
738
+ # Add this function to format the stats display
739
+ def create_stats_display(stats):
740
+ # Current Age Section
741
+ current_age = f"""
742
+ ### 🎂 Current Age
743
+ - **Years:** {stats["Current Age"]["Years"]} years
744
+ - **Months:** {stats["Current Age"]["Months"]} months
745
+ - **Weeks:** {stats["Current Age"]["Weeks"]} weeks
746
+ - **Days:** {stats["Current Age"]["Days"]} days
747
+
748
+ ⏰ You've been alive for:
749
+ - **{stats["Current Age"]["Hours"]}** hours
750
+ - **{stats["Current Age"]["Minutes"]}** minutes
751
+ """
752
+
753
+ # Time Until 90 Section
754
+ time_remaining = f"""
755
+ ### ⏳ Time Until 90
756
+ - **Years:** {stats["Time Until 90"]["Years"]} years
757
+ - **Months:** {stats["Time Until 90"]["Months"]} months
758
+ - **Weeks:** {stats["Time Until 90"]["Weeks"]} weeks
759
+ - **Days:** {stats["Time Until 90"]["Days"]} days
760
+ """
761
+
762
+ # Progress Section
763
+ progress = f"""
764
+ ### 📊 Life Progress
765
+ - **Life Journey:** {stats["Life Progress"]["Life Percentage"]} complete
766
+ - **This Year:** {stats["Life Progress"]["Current Year"]} complete
767
+ - **This Month:** {stats["Life Progress"]["Current Month"]} complete
768
+ - **This Week:** {stats["Life Progress"]["Current Week"]} complete
769
+ - **Today:** {stats["Life Progress"]["Current Day"]} complete
770
+ """
771
+
772
+ # Milestones Section
773
+ milestones = f"""
774
+ ### 🏆 Key Milestones
775
+ - **Next Birthday:** {stats["Milestones"]["Days to Next Birthday"]} days away
776
+ - **Weeks Journey:** {stats["Milestones"]["Weeks Lived"]} weeks lived
777
+ - **Days Journey:** {stats["Milestones"]["Days Lived"]} days experienced
778
+ - **Hours Journey:** {stats["Milestones"]["Hours Lived"]} hours lived
779
+ - **10k Milestone:** {stats["Milestones"]["10,000 Days"]}
780
+ - **Life Season:** {stats["Milestones"]["Life Seasons"]}
781
+ """
782
+
783
+ return current_age, time_remaining, progress, milestones
784
+
785
+ # Add this function to format daily data for display
786
+ def format_daily_summary(date, data):
787
+ """Format daily data into a summary row for the history table"""
788
+ try:
789
+ metrics = data.get("tracking_metrics", {})
790
+ habits_completed = sum(1 for habit in data.get("habits", {}).values()
791
+ for done in habit if done)
792
+ total_habits = len(data.get("habits", {})) * 7
793
+ habits_percent = f"{(habits_completed/total_habits)*100:.1f}%" if total_habits > 0 else "0%"
794
+
795
+ journal_entries = sum(1 for entry in data.get("journal", {}).values() if entry.strip())
796
+
797
+ # Format date for display
798
+ display_date = datetime.datetime.strptime(date, "%Y-%m-%d").strftime("%Y-%m-%d (%a)")
799
+
800
+ return [
801
+ display_date,
802
+ metrics.get("productivity", 0),
803
+ metrics.get("energy", 0),
804
+ metrics.get("mood", 0),
805
+ metrics.get("sleep_quality", 0),
806
+ metrics.get("exercise_intensity", 0),
807
+ habits_percent,
808
+ journal_entries
809
+ ]
810
+ except Exception as e:
811
+ print(f"Error formatting data for {date}: {e}")
812
+ return [date, 0, 0, 0, 0, 0, "0%", 0]
813
+
814
+ # Add this function to load history data
815
+ def load_history_data():
816
+ """Load all available daily data files"""
817
+ history_data = []
818
+
819
+ # Get all JSON files in the data directory
820
+ json_files = sorted(DATA_DIR.glob("*.json"), reverse=True) # Sort newest first
821
+
822
+ for file_path in json_files:
823
+ date = file_path.stem # Get date from filename
824
+ try:
825
+ with open(file_path, 'r') as f:
826
+ data = json.load(f)
827
+ # Format the data for display
828
+ history_data.append(format_daily_summary(date, data))
829
+ except Exception as e:
830
+ print(f"Error loading {file_path}: {e}")
831
+
832
+ return history_data
833
+
834
+ def save_user_config(birth_date, birth_time):
835
+ """Save user configuration data"""
836
+ try:
837
+ # Validate date format
838
+ datetime.datetime.strptime(birth_date, "%Y-%m-%d")
839
+
840
+ # Convert time to 24-hour format
841
+ formatted_time = parse_time(birth_time)
842
+
843
+ config = {
844
+ "birth_date": birth_date,
845
+ "birth_time": formatted_time
846
+ }
847
+
848
+ config_path = Path("user_config.json")
849
+ with open(config_path, "w") as f:
850
+ json.dump(config, f, indent=2)
851
+
852
+ return True
853
+ except Exception as e:
854
+ print(f"Error saving config: {e}")
855
+ return False
856
+
857
+ def load_user_config():
858
+ """Load user configuration data"""
859
+ config_path = Path("user_config.json")
860
+ default_config = {
861
+ "birth_date": "1988-12-24", # Your birth date
862
+ "birth_time": "11:37" # Your birth time in 24-hour format
863
+ }
864
+
865
+ if config_path.exists():
866
+ try:
867
+ with open(config_path, "r") as f:
868
+ config = json.load(f)
869
+ # Validate loaded data
870
+ datetime.datetime.strptime(config["birth_date"], "%Y-%m-%d")
871
+ parse_time(config["birth_time"])
872
+ return config
873
+ except Exception as e:
874
+ print(f"Error loading config: {e}")
875
+
876
+ return default_config
877
+
878
+ def create_habit_section():
879
+ """Create the habit tracking section with better layout"""
880
+ # Load current habits
881
+ current_habits = load_habits()
882
+
883
+ # Group habits by time of day
884
+ morning_habits = [h for h in current_habits if any(x in h.lower() for x in ["wake", "morning", "meditation", "brain", "walk"])]
885
+ evening_habits = [h for h in current_habits if any(x in h.lower() for x in ["evening", "massage", "learning", "journal", "guitar"])]
886
+ flexible_habits = [h for h in current_habits if h not in morning_habits + evening_habits]
887
+
888
+ with gr.Column(scale=1):
889
+ gr.Markdown("### 📅 Daily Habits & Tasks")
890
+
891
+ # Create summary text component first
892
+ with gr.Row():
893
+ gr.Markdown("### 📊 Weekly Summary")
894
+ summary_text = gr.Markdown()
895
+ refresh_btn = gr.Button("🔄 Refresh")
896
+
897
+ # Connect refresh button to update summary
898
+ refresh_btn.click(
899
+ fn=update_summary,
900
+ outputs=summary_text
901
+ )
902
+
903
+ # Add column headers once at the top
904
+ with gr.Row(equal_height=True, variant="compact"):
905
+ with gr.Column(scale=6):
906
+ gr.Markdown("Task")
907
+ with gr.Column(scale=7):
908
+ with gr.Row(equal_height=True, variant="compact"):
909
+ days = ["M", "T", "W", "Th", "F", "S", "Su"]
910
+ for day in days:
911
+ with gr.Column(scale=1, min_width=35):
912
+ gr.Markdown(day)
913
+ with gr.Column(scale=3):
914
+ gr.Markdown("Progress")
915
+
916
+ # Morning Section
917
+ with gr.Group():
918
+ gr.Markdown("#### 🌅 Morning Routine")
919
+ create_habit_group(morning_habits, summary_text) # Pass summary_text as parameter
920
+
921
+ gr.Markdown("---")
922
+
923
+ # Evening Section
924
+ with gr.Group():
925
+ gr.Markdown("#### 🌙 Evening Activities")
926
+ create_habit_group(evening_habits, summary_text) # Pass summary_text as parameter
927
+
928
+ gr.Markdown("---")
929
+
930
+ # Flexible Section
931
+ with gr.Group():
932
+ gr.Markdown("#### ⭐ Flexible Activities")
933
+ create_habit_group(flexible_habits, summary_text) # Pass summary_text as parameter
934
+
935
+ gr.Markdown("---")
936
+
937
+ # Add new habit section
938
+ with gr.Row(equal_height=True):
939
+ new_habit = gr.Textbox(
940
+ label="New Habit/Task",
941
+ placeholder="Enter a new habit or task to track",
942
+ scale=4,
943
+ min_width=250
944
+ )
945
+ duration = gr.Dropdown(
946
+ choices=["", "15min", "20min", "30min", "45min", "60min"],
947
+ label="Duration",
948
+ scale=2,
949
+ min_width=100
950
+ )
951
+ add_btn = gr.Button("Add Habit", scale=1, min_width=100)
952
+
953
+ def create_habit_group(habits, summary_text):
954
+ """Create a group of habit tracking rows with better scaling"""
955
+ for habit in habits:
956
+ with gr.Row(equal_height=True, variant="compact") as row:
957
+ # Task name and duration (left side)
958
+ with gr.Column(scale=6, min_width=300):
959
+ name_edit = gr.Textbox(
960
+ value=habit,
961
+ label="Task Name",
962
+ interactive=True,
963
+ container=False,
964
+ )
965
+
966
+ # Duration slider (optional, shown inline)
967
+ duration_edit = None
968
+ if any(x in habit.lower() for x in ["min", "hour"]):
969
+ duration_edit = gr.Slider(
970
+ minimum=5,
971
+ maximum=60,
972
+ value=int(''.join(filter(str.isdigit, habit))) if any(c.isdigit() for c in habit) else 20,
973
+ step=5,
974
+ label="Duration",
975
+ container=False,
976
+ scale=1
977
+ )
978
+
979
+ # Checkboxes container (middle)
980
+ with gr.Column(scale=7, min_width=350):
981
+ with gr.Row(equal_height=True, variant="compact"):
982
+ days = ["M", "T", "W", "Th", "F", "S", "Su"]
983
+ checkboxes = []
984
+ for day in days:
985
+ with gr.Column(scale=1, min_width=30):
986
+ checkbox = gr.Checkbox(
987
+ label=day,
988
+ container=False,
989
+ scale=1,
990
+ min_width=25,
991
+ show_label=False
992
+ )
993
+ gr.Markdown(day, container=False)
994
+ checkboxes.append(checkbox)
995
+
996
+ # Progress and actions (right side)
997
+ with gr.Column(scale=3, min_width=150):
998
+ with gr.Row(equal_height=True, variant="compact"):
999
+ progress = gr.Textbox(
1000
+ value="0%",
1001
+ label="Progress",
1002
+ interactive=False,
1003
+ container=False,
1004
+ scale=2
1005
+ )
1006
+ edit_btn = gr.Button("✏️", scale=1, min_width=30)
1007
+ delete_btn = gr.Button("🗑️", scale=1, min_width=30)
1008
+
1009
+ # Connect edit button handler
1010
+ def edit_habit(name, duration=None):
1011
+ if duration is not None:
1012
+ result = update_habit_name(habit, name, duration)
1013
+ gr.Info(f"Updated: {name}")
1014
+ return result
1015
+ result = update_habit_name(habit, name)
1016
+ gr.Info(f"Updated: {name}")
1017
+ return result
1018
+
1019
+ edit_btn.click(
1020
+ fn=edit_habit,
1021
+ inputs=[name_edit] + ([duration_edit] if duration_edit else []),
1022
+ outputs=[name_edit]
1023
+ )
1024
+
1025
+ # Connect delete button handler
1026
+ def delete_habit_fn(habit_name=habit):
1027
+ """Delete a habit and update the UI"""
1028
+ try:
1029
+ # 1. Update current daily data
1030
+ current_data = load_daily_data()
1031
+ if habit_name in current_data["habits"]:
1032
+ del current_data["habits"][habit_name]
1033
+ save_daily_data(current_data)
1034
+
1035
+ # 2. Update habits configuration
1036
+ current_habits = load_habits()
1037
+ if habit_name in current_habits:
1038
+ current_habits.remove(habit_name)
1039
+ save_habits(current_habits)
1040
+
1041
+ # 3. Update all existing daily data files to remove this habit
1042
+ update_all_daily_files(habit_to_remove=habit_name)
1043
+
1044
+ # Show notification
1045
+ gr.Info(f"Deleted: {habit_name}")
1046
+ return gr.update(visible=False)
1047
+ except Exception as e:
1048
+ print(f"Error deleting habit: {e}")
1049
+ gr.Warning(f"Failed to delete {habit_name}")
1050
+ return gr.update()
1051
+
1052
+ # Connect delete button with proper output
1053
+ delete_btn.click(
1054
+ fn=delete_habit_fn,
1055
+ outputs=row
1056
+ )
1057
+
1058
+ # Update progress when checkboxes change
1059
+ def make_progress_handler(current_habit):
1060
+ """Create a closure for the progress handler to capture the habit name"""
1061
+ def update_progress_display(*checkbox_values):
1062
+ """Update progress display for a habit"""
1063
+ completed = sum(1 for x in checkbox_values if x)
1064
+ total = len(checkbox_values)
1065
+ percentage = f"{(completed/total)*100:.1f}%" if total > 0 else "0%"
1066
+
1067
+ # Update the data
1068
+ current_data = load_daily_data()
1069
+ if current_habit in current_data["habits"]:
1070
+ current_data["habits"][current_habit] = list(checkbox_values)
1071
+ save_daily_data(current_data)
1072
+
1073
+ return percentage
1074
+ return update_progress_display
1075
+
1076
+ # Connect checkbox handlers using closure
1077
+ progress_handler = make_progress_handler(habit)
1078
+ for checkbox in checkboxes:
1079
+ checkbox.change(
1080
+ fn=progress_handler,
1081
+ inputs=checkboxes,
1082
+ outputs=progress
1083
+ )
1084
+
1085
+ def update_habit_name(old_name, new_name, duration=None):
1086
+ """Update habit name and optionally duration"""
1087
+ if not new_name.strip():
1088
+ return gr.update()
1089
+
1090
+ try:
1091
+ # Format new name
1092
+ new_full_name = new_name.strip()
1093
+ if duration is not None:
1094
+ new_full_name = f"{new_full_name} ({int(duration)}min)"
1095
+
1096
+ # Update habits configuration
1097
+ current_habits = load_habits()
1098
+ if old_name in current_habits:
1099
+ current_habits.remove(old_name)
1100
+ current_habits.append(new_full_name)
1101
+ save_habits(current_habits)
1102
+
1103
+ # Update all daily files
1104
+ update_all_daily_files(habit_to_rename=old_name, new_name=new_full_name)
1105
+
1106
+ # Return update for the textbox
1107
+ return gr.update(value=new_full_name)
1108
+ except Exception as e:
1109
+ print(f"Error updating habit name: {e}")
1110
+ gr.Warning(f"Failed to update {old_name}")
1111
+ return gr.update()
1112
+
1113
+ def update_summary():
1114
+ """Update the weekly summary text"""
1115
+ current_data = load_daily_data()
1116
+ total_habits = len(current_data["habits"])
1117
+ total_completed = sum(
1118
+ sum(1 for x in habit_data if x)
1119
+ for habit_data in current_data["habits"].values()
1120
+ )
1121
+ total_possible = total_habits * 7
1122
+ completion_rate = (total_completed / total_possible * 100) if total_possible > 0 else 0
1123
+
1124
+ return f"""
1125
+ - Total Habits: {total_habits}
1126
+ - Completed Tasks: {total_completed}/{total_possible}
1127
+ - Overall Completion Rate: {completion_rate:.1f}%
1128
+ """
1129
+
1130
+ def auto_refresh():
1131
+ """Auto refresh function to check and refresh day data"""
1132
+ try:
1133
+ current_data = load_daily_data()
1134
+ current_data = check_and_refresh_day(current_data)
1135
+ save_daily_data(current_data)
1136
+ # Don't return anything
1137
+ except Exception as e:
1138
+ print(f"Error in auto refresh: {e}")
1139
+
1140
+ def update_all_daily_files(habit_to_remove=None, habit_to_rename=None, new_name=None):
1141
+ """Update all daily data files when habits are modified"""
1142
+ try:
1143
+ # Get all JSON files in the data directory
1144
+ json_files = list(DATA_DIR.glob("*.json"))
1145
+
1146
+ for file_path in json_files:
1147
+ try:
1148
+ # Load the file
1149
+ with open(file_path, 'r') as f:
1150
+ data = json.load(f)
1151
+
1152
+ modified = False
1153
+
1154
+ # Handle habit removal
1155
+ if habit_to_remove and habit_to_remove in data.get("habits", {}):
1156
+ del data["habits"][habit_to_remove]
1157
+ modified = True
1158
+
1159
+ # Handle habit renaming
1160
+ if habit_to_rename and new_name and habit_to_rename in data.get("habits", {}):
1161
+ data["habits"][new_name] = data["habits"].pop(habit_to_rename)
1162
+ modified = True
1163
+
1164
+ # Save if modified
1165
+ if modified:
1166
+ with open(file_path, 'w') as f:
1167
+ json.dump(data, f, indent=2, sort_keys=True)
1168
+
1169
+ except Exception as e:
1170
+ print(f"Error updating file {file_path}: {e}")
1171
+ continue
1172
+ except Exception as e:
1173
+ print(f"Error updating daily files: {e}")
1174
+
1175
+ def get_available_dates():
1176
+ """Get list of dates with existing data files"""
1177
+ try:
1178
+ json_files = sorted(DATA_DIR.glob("*.json"), reverse=True) # Most recent first
1179
+ dates = [file.stem for file in json_files] # Get dates from filenames
1180
+ return dates
1181
+ except Exception as e:
1182
+ print(f"Error getting available dates: {e}")
1183
+ return [get_date_key()] # Return today's date as fallback
1184
+
1185
+ def refresh_dates():
1186
+ """Refresh the available dates in the dropdown"""
1187
+ dates = get_available_dates()
1188
+ return gr.update(choices=dates)
1189
+
1190
+ def load_selected_date_metrics(date):
1191
+ """Load metrics for the selected date"""
1192
+ data = load_daily_data(date)
1193
+ metrics = data.get("tracking_metrics", {
1194
+ "productivity": 0,
1195
+ "energy": 0,
1196
+ "mood": 0,
1197
+ "sleep_quality": 0,
1198
+ "exercise_intensity": 0
1199
+ })
1200
+
1201
+ return [
1202
+ metrics["productivity"],
1203
+ metrics["energy"],
1204
+ metrics["mood"],
1205
+ metrics["sleep_quality"],
1206
+ metrics["exercise_intensity"]
1207
+ ]
1208
+
1209
+ def load_day_details(evt: gr.SelectData):
1210
+ """Load detailed view of a specific day"""
1211
+ try:
1212
+ # Get clicked date (remove the day of week if present)
1213
+ date = evt.value[0].split(" (")[0] if isinstance(evt.value, list) else evt.value
1214
+
1215
+ data = load_daily_data(date)
1216
+
1217
+ # Ensure required data structures exist with defaults
1218
+ if "focus" not in data:
1219
+ data["focus"] = {
1220
+ "priorities": [],
1221
+ "later": [],
1222
+ "priority_reward": "",
1223
+ "later_reward": ""
1224
+ }
1225
+
1226
+ if "meals" not in data:
1227
+ data["meals"] = {
1228
+ "breakfast": "",
1229
+ "lunch": "",
1230
+ "dinner": "",
1231
+ "snacks": ""
1232
+ }
1233
+
1234
+ # Ensure focus lists exist and are properly formatted
1235
+ if not isinstance(data["focus"].get("priorities", []), list):
1236
+ data["focus"]["priorities"] = []
1237
+ if not isinstance(data["focus"].get("later", []), list):
1238
+ data["focus"]["later"] = []
1239
+
1240
+ # Format metrics data
1241
+ metrics_data = [
1242
+ ["Productivity", data.get("tracking_metrics", {}).get("productivity", 0)],
1243
+ ["Energy", data.get("tracking_metrics", {}).get("energy", 0)],
1244
+ ["Mood", data.get("tracking_metrics", {}).get("mood", 0)],
1245
+ ["Sleep Quality", data.get("tracking_metrics", {}).get("sleep_quality", 0)],
1246
+ ["Exercise Intensity", data.get("tracking_metrics", {}).get("exercise_intensity", 0)]
1247
+ ]
1248
+
1249
+ # Format habits data with proper validation
1250
+ habits_data = []
1251
+ habits_dict = data.get("habits", {})
1252
+
1253
+ # Load current habits configuration to ensure we show all configured habits
1254
+ current_habits = load_habits()
1255
+
1256
+ # Create a row for each habit, using stored data if available or default values if not
1257
+ for habit in current_habits:
1258
+ status = habits_dict.get(habit, [False] * 7)
1259
+ # Ensure status is a list of 7 boolean values
1260
+ if not isinstance(status, list) or len(status) != 7:
1261
+ status = [False] * 7
1262
+ row = [habit]
1263
+ row.extend(["✓" if done else "×" for done in status])
1264
+ habits_data.append(row)
1265
+
1266
+ # Sort habits by category (morning, evening, flexible)
1267
+ def get_habit_category(habit):
1268
+ if any(x in habit.lower() for x in ["wake", "morning", "meditation", "brain", "walk"]):
1269
+ return 0 # Morning
1270
+ elif any(x in habit.lower() for x in ["evening", "massage", "learning", "journal", "guitar"]):
1271
+ return 1 # Evening
1272
+ return 2 # Flexible
1273
+
1274
+ habits_data.sort(key=lambda x: get_habit_category(x[0]))
1275
+
1276
+ # Format journal data with validation
1277
+ journal_data = []
1278
+ for q, a in data.get("journal", {}).items():
1279
+ if isinstance(a, str):
1280
+ if a.strip():
1281
+ journal_data.append([q, a])
1282
+
1283
+ # Format focus and meals data with proper validation
1284
+ priorities_text = "\n".join([
1285
+ f"- {task[0]}: {task[1]}" if isinstance(task, list) and len(task) >= 2 else "- No task"
1286
+ for task in data["focus"]["priorities"]
1287
+ if isinstance(task, list) and len(task) >= 2 and task[0]
1288
+ ]) or "No priorities set"
1289
+
1290
+ later_text = "\n".join([
1291
+ f"- {task[0]}: {task[1]}" if isinstance(task, list) and len(task) >= 2 else "- No task"
1292
+ for task in data["focus"]["later"]
1293
+ if isinstance(task, list) and len(task) >= 2 and task[0]
1294
+ ]) or "No tasks set"
1295
+
1296
+ focus_meals = f"""### 🎯 Focus Tasks
1297
+
1298
+ **Must Do Today:**
1299
+ {priorities_text}
1300
+
1301
+ **For Later:**
1302
+ {later_text}
1303
+
1304
+ ### 🍽️ Meals
1305
+ - **Breakfast:** {data["meals"].get("breakfast", "") or "Not recorded"}
1306
+ - **Lunch:** {data["meals"].get("lunch", "") or "Not recorded"}
1307
+ - **Dinner:** {data["meals"].get("dinner", "") or "Not recorded"}
1308
+ - **Snacks:** {data["meals"].get("snacks", "") or "Not recorded"}
1309
+ """
1310
+
1311
+ return [
1312
+ date, # Selected date
1313
+ metrics_data, # Metrics view
1314
+ habits_data, # Habits view
1315
+ journal_data, # Journal view
1316
+ focus_meals # Focus and meals view
1317
+ ]
1318
+ except Exception as e:
1319
+ print(f"Error loading details for date {date if 'date' in locals() else 'unknown'}: {str(e)}")
1320
+ # Return a graceful fallback with empty/default values
1321
+ return [
1322
+ evt.value,
1323
+ [["No metrics data available", 0]],
1324
+ [["No habits data available", "×", "×", "×", "×", "×", "×", "×"]],
1325
+ [["No journal entries available", ""]],
1326
+ """### 🎯 Focus Tasks\n\n**Must Do Today:**\nNo data available\n\n**For Later:**\nNo data available\n\n### 🍽️ Meals\nNo meal data available"""
1327
+ ]
1328
+
1329
+ def manual_save():
1330
+ """Manually save all current data"""
1331
+ try:
1332
+ current_data = load_daily_data()
1333
+ save_daily_data(current_data)
1334
+ return "✅ Data saved successfully!"
1335
+ except Exception as e:
1336
+ print(f"Error saving data: {e}")
1337
+ return "❌ Error saving data"
1338
+
1339
+ with gr.Blocks(theme='Nymbo/Nymbo_Theme') as demo:
1340
+ # Load initial data
1341
+ current_data = load_daily_data()
1342
+ current_data = check_and_refresh_day(current_data)
1343
+
1344
+
1345
+ # Centered, smaller logo
1346
+ with gr.Row():
1347
+ with gr.Column(scale=2):
1348
+ pass # Empty column for spacing
1349
+ with gr.Column(scale=1):
1350
+ gr.Image("./logo.png", show_label=False, container=False, width=250, show_download_button=False)
1351
+ with gr.Column(scale=2):
1352
+ pass # Empty column for spacing
1353
+
1354
+ with gr.Row():
1355
+ date_text = gr.Textbox(
1356
+ value=datetime.datetime.now().strftime("%A, %B %d"),
1357
+ label="Date",
1358
+ interactive=False
1359
+ )
1360
+ date_picker = gr.Dropdown(
1361
+ choices=get_available_dates(),
1362
+ value=get_date_key(),
1363
+ label="Select Date",
1364
+ interactive=True
1365
+ )
1366
+ today_btn = gr.Button("Return to Today")
1367
+ refresh_dates_btn = gr.Button("🔄 Refresh Dates")
1368
+ save_btn = gr.Button("💾 Save Data", variant="primary")
1369
+ save_status = gr.Markdown(visible=True)
1370
+
1371
+ # Connect save button event
1372
+ save_btn.click(
1373
+ fn=manual_save,
1374
+ outputs=save_status
1375
+ ).then(
1376
+ lambda: gr.update(visible=True),
1377
+ None,
1378
+ save_status
1379
+ )
1380
+
1381
+ # Create state for all components that need updating
1382
+ with gr.Tabs():
1383
+ # Must Do Daily Tab
1384
+ with gr.TabItem("Must Do Daily"):
1385
+ create_habit_section()
1386
+
1387
+ # Today's Focus Tab
1388
+ with gr.TabItem("Today's Focus"):
1389
+ with gr.Column():
1390
+ gr.Markdown("### Today's Focus")
1391
+ with gr.Row():
1392
+ # Left column for priorities
1393
+ with gr.Column(scale=2):
1394
+ gr.Markdown("#### Must Do Today")
1395
+ priorities = gr.Dataframe(
1396
+ headers=["Task", "Status"],
1397
+ datatype=["str", "str"],
1398
+ value=current_data["focus"]["priorities"],
1399
+ row_count=5, # Fixed number of rows
1400
+ col_count=(2, "fixed"), # Fixed number of columns
1401
+ interactive=True
1402
+ )
1403
+ priority_reward = gr.Textbox(
1404
+ label="Reward for Completion",
1405
+ value=current_data["focus"]["priority_reward"],
1406
+ placeholder="Enter reward for completing priorities",
1407
+ lines=2
1408
+ )
1409
+
1410
+ # Right column for later tasks
1411
+ with gr.Column(scale=2):
1412
+ gr.Markdown("#### For Later")
1413
+ later = gr.Dataframe(
1414
+ headers=["Task", "Status"],
1415
+ datatype=["str", "str"],
1416
+ value=current_data["focus"]["later"],
1417
+ row_count=5, # Fixed number of rows
1418
+ col_count=(2, "fixed"), # Fixed number of columns
1419
+ interactive=True
1420
+ )
1421
+ later_reward = gr.Textbox(
1422
+ label="Reward for Completion",
1423
+ value=current_data["focus"]["later_reward"],
1424
+ placeholder="Enter reward for completing later tasks",
1425
+ lines=2
1426
+ )
1427
+
1428
+ # Event handlers
1429
+ priorities.change(
1430
+ lambda x: update_data("focus.priorities", x),
1431
+ inputs=[priorities]
1432
+ )
1433
+ priority_reward.change(
1434
+ lambda x: update_data("focus.priority_reward", x),
1435
+ inputs=[priority_reward]
1436
+ )
1437
+ later.change(
1438
+ lambda x: update_data("focus.later", x),
1439
+ inputs=[later]
1440
+ )
1441
+ later_reward.change(
1442
+ lambda x: update_data("focus.later_reward", x),
1443
+ inputs=[later_reward]
1444
+ )
1445
+
1446
+ # Meals Tab
1447
+ with gr.TabItem("Meals"):
1448
+ with gr.Column():
1449
+ meal_inputs = {}
1450
+ for meal in ["breakfast", "lunch", "dinner", "snacks"]:
1451
+ meal_inputs[meal] = gr.Textbox(
1452
+ label=meal.capitalize(),
1453
+ value=current_data["meals"][meal],
1454
+ placeholder=f"What did you have for {meal}?"
1455
+ )
1456
+ meal_inputs[meal].change(
1457
+ lambda x, m=meal: update_data(f"meals.{m}", x),
1458
+ inputs=[meal_inputs[meal]]
1459
+ )
1460
+
1461
+ # Life Overview Tab
1462
+ with gr.TabItem("Life Overview"):
1463
+ with gr.Column():
1464
+ gr.Markdown("""
1465
+ ### 🌟 Your Life Journey Calculator
1466
+
1467
+ Time is our most precious resource - not because it's limited, but because it's an opportunity for endless possibilities.
1468
+ Let's explore your unique journey through time and discover the incredible potential that lies ahead.
1469
+ """)
1470
+
1471
+ # Load saved config
1472
+ user_config = load_user_config()
1473
+
1474
+ with gr.Row():
1475
+ birth_date = gr.Textbox(
1476
+ label="Your Birth Date (YYYY-MM-DD)",
1477
+ value=user_config["birth_date"],
1478
+ placeholder="YYYY-MM-DD"
1479
+ )
1480
+ birth_time = gr.Textbox(
1481
+ label="Your Birth Time",
1482
+ value=user_config["birth_time"],
1483
+ placeholder="HH:MM (24-hour) or HH:MM AM/PM"
1484
+ )
1485
+ calculate_btn = gr.Button("✨ Calculate Your Life Journey", variant="primary")
1486
+
1487
+ # Add status message
1488
+ status_msg = gr.Markdown()
1489
+
1490
+ with gr.Tabs():
1491
+ with gr.TabItem("Life Stats"):
1492
+ with gr.Row():
1493
+ current_age_md = gr.Markdown()
1494
+ time_remaining_md = gr.Markdown()
1495
+ with gr.Row():
1496
+ progress_md = gr.Markdown()
1497
+ milestones_md = gr.Markdown()
1498
+
1499
+ with gr.TabItem("Visualizations"):
1500
+ with gr.Row():
1501
+ progress_plot = gr.Plot(label="Life Progress")
1502
+ remaining_plot = gr.Plot(label="Time Until 90")
1503
+ timeline_plot = gr.Plot(label="Life Seasons Timeline")
1504
+
1505
+ # Update the calculate button click handler
1506
+ def update_stats_and_plots(birth_date_str, birth_time_str):
1507
+ try:
1508
+ # Save the values
1509
+ if save_user_config(birth_date_str, birth_time_str):
1510
+ stats = calculate_age_stats(birth_date_str, birth_time_str)
1511
+ if "Error" in stats:
1512
+ return [
1513
+ f"⚠️ {stats['Error']}", "", "", "", None, None, None
1514
+ ]
1515
+
1516
+ figures = create_life_visualizations(stats)
1517
+ current_age, time_remaining, progress, milestones = create_stats_display(stats)
1518
+ return [
1519
+ current_age,
1520
+ time_remaining,
1521
+ progress,
1522
+ milestones,
1523
+ figures["progress"],
1524
+ figures["remaining"],
1525
+ figures["timeline"]
1526
+ ]
1527
+ else:
1528
+ return ["⚠️ Failed to save configuration", "", "", "", None, None, None]
1529
+ except Exception as e:
1530
+ return [f"⚠️ Error: {str(e)}", "", "", "", None, None, None]
1531
+
1532
+ calculate_btn.click(
1533
+ fn=update_stats_and_plots,
1534
+ inputs=[birth_date, birth_time],
1535
+ outputs=[
1536
+ current_age_md,
1537
+ time_remaining_md,
1538
+ progress_md,
1539
+ milestones_md,
1540
+ progress_plot,
1541
+ remaining_plot,
1542
+ timeline_plot
1543
+ ]
1544
+ )
1545
+
1546
+ # Progress Tracking Tab
1547
+ with gr.TabItem("Progress Tracking"):
1548
+ with gr.Column():
1549
+ gr.Markdown("### Daily Metrics")
1550
+ with gr.Row():
1551
+ # Load data for the selected date
1552
+ selected_data = load_daily_data(date_picker.value)
1553
+
1554
+ if "tracking_metrics" not in selected_data:
1555
+ selected_data["tracking_metrics"] = {
1556
+ "productivity": 0,
1557
+ "energy": 0,
1558
+ "mood": 0,
1559
+ "sleep_quality": 0,
1560
+ "exercise_intensity": 0
1561
+ }
1562
+
1563
+ productivity = gr.Slider(minimum=0, maximum=5, value=selected_data["tracking_metrics"]["productivity"], label="Productivity", step=1)
1564
+ energy = gr.Slider(minimum=0, maximum=5, value=selected_data["tracking_metrics"]["energy"], label="Energy Level", step=1)
1565
+ mood = gr.Slider(minimum=0, maximum=5, value=selected_data["tracking_metrics"]["mood"], label="Mood", step=1)
1566
+ sleep = gr.Slider(minimum=0, maximum=5, value=selected_data["tracking_metrics"]["sleep_quality"], label="Sleep Quality", step=1)
1567
+ exercise = gr.Slider(minimum=0, maximum=5, value=selected_data["tracking_metrics"]["exercise_intensity"], label="Exercise Intensity", step=1)
1568
+
1569
+ gr.Markdown("### Weekly Overview")
1570
+ week_data = load_week_data()
1571
+ week_dates = get_week_dates()
1572
+ weekly_grid = gr.DataFrame(
1573
+ headers=["Metric"] + [datetime.datetime.strptime(date, "%Y-%m-%d").strftime("%a %d") for date in week_dates],
1574
+ value=[
1575
+ ["Productivity"] + [week_data[date]["tracking_metrics"]["productivity"] for date in week_dates],
1576
+ ["Energy"] + [week_data[date]["tracking_metrics"]["energy"] for date in week_dates],
1577
+ ["Mood"] + [week_data[date]["tracking_metrics"]["mood"] for date in week_dates],
1578
+ ["Sleep"] + [week_data[date]["tracking_metrics"]["sleep_quality"] for date in week_dates],
1579
+ ["Exercise"] + [week_data[date]["tracking_metrics"]["exercise_intensity"] for date in week_dates]
1580
+ ],
1581
+ interactive=False
1582
+ )
1583
+
1584
+ gr.Markdown("### Monthly Stats")
1585
+ month_data = load_month_data()
1586
+ with gr.Row():
1587
+ prod_label = gr.Label(f"Average Productivity: {calculate_success_rate(month_data, 'productivity')}/10")
1588
+ energy_label = gr.Label(f"Average Energy: {calculate_success_rate(month_data, 'energy')}/10")
1589
+ mood_label = gr.Label(f"Average Mood: {calculate_success_rate(month_data, 'mood')}/10")
1590
+ sleep_label = gr.Label(f"Average Sleep: {calculate_success_rate(month_data, 'sleep_quality')}/10")
1591
+ exercise_label = gr.Label(f"Average Exercise: {calculate_success_rate(month_data, 'exercise_intensity')}/10")
1592
+
1593
+ # Update tracking metrics and connect to UI elements
1594
+ for slider in [productivity, energy, mood, sleep, exercise]:
1595
+ slider.change(
1596
+ fn=update_metrics,
1597
+ inputs=[productivity, energy, mood, sleep, exercise, date_picker],
1598
+ outputs=[
1599
+ weekly_grid,
1600
+ prod_label,
1601
+ energy_label,
1602
+ mood_label,
1603
+ sleep_label,
1604
+ exercise_label
1605
+ ]
1606
+ )
1607
+
1608
+ # Journaling Tab
1609
+ with gr.TabItem("Daily Journal"):
1610
+ with gr.Column():
1611
+ gr.Markdown("""
1612
+ ### 📝 Daily Reflection Journal
1613
+
1614
+ Take a moment to reflect on your day. These questions will help you process your experiences,
1615
+ learn from them, and plan for tomorrow. Your responses are saved automatically.
1616
+ """)
1617
+
1618
+ # Create journal entries
1619
+ journal_inputs = {}
1620
+ current_journal = current_data.get("journal", {})
1621
+
1622
+ for question in JOURNAL_QUESTIONS:
1623
+ with gr.Group():
1624
+ gr.Markdown(f"#### {question}")
1625
+ journal_inputs[question] = gr.TextArea(
1626
+ value=current_journal.get(question, ""),
1627
+ placeholder="Write your thoughts here...",
1628
+ lines=3,
1629
+ label=""
1630
+ )
1631
+
1632
+ # Add change handler for each text area
1633
+ journal_inputs[question].change(
1634
+ fn=update_journal_entry,
1635
+ inputs=[
1636
+ gr.State(question),
1637
+ journal_inputs[question]
1638
+ ]
1639
+ )
1640
+
1641
+ # Add export button
1642
+ def export_journal():
1643
+ current_data = load_daily_data()
1644
+ if "journal" in current_data:
1645
+ date = get_date_key()
1646
+ export_text = f"Journal Entry for {date}\n\n"
1647
+
1648
+ for question in JOURNAL_QUESTIONS:
1649
+ answer = current_data["journal"].get(question, "")
1650
+ if answer: # Only include questions with answers
1651
+ export_text += f"Q: {question}\n"
1652
+ export_text += f"A: {answer}\n\n"
1653
+
1654
+ return export_text
1655
+ return "No journal entries found for today."
1656
+
1657
+ with gr.Row():
1658
+ export_btn = gr.Button("Export Journal Entry")
1659
+ export_text = gr.TextArea(
1660
+ label="Exported Journal",
1661
+ interactive=False,
1662
+ visible=False
1663
+ )
1664
+
1665
+ export_btn.click(
1666
+ fn=export_journal,
1667
+ outputs=export_text,
1668
+ show_progress=True
1669
+ ).then(
1670
+ lambda: gr.update(visible=True),
1671
+ None,
1672
+ [export_text]
1673
+ )
1674
+
1675
+ # History Tab
1676
+ with gr.TabItem("History"):
1677
+ with gr.Column():
1678
+ gr.Markdown("### 📚 History Viewer")
1679
+
1680
+ # Create the history table
1681
+ history_headers = [
1682
+ "Date",
1683
+ "Productivity",
1684
+ "Energy",
1685
+ "Mood",
1686
+ "Sleep",
1687
+ "Exercise",
1688
+ "Habits",
1689
+ "Journal"
1690
+ ]
1691
+
1692
+ with gr.Row():
1693
+ history_table = gr.DataFrame(
1694
+ headers=history_headers,
1695
+ datatype=["str", "number", "number", "number", "number", "number", "str", "number"],
1696
+ value=load_history_data(),
1697
+ interactive=False
1698
+ )
1699
+
1700
+ # Create detail view sections
1701
+ with gr.Row():
1702
+ selected_date = gr.Textbox(label="Selected Date", interactive=False)
1703
+ refresh_btn = gr.Button("🔄 Refresh History")
1704
+
1705
+ with gr.Tabs() as detail_tabs:
1706
+ with gr.TabItem("Metrics"):
1707
+ metrics_view = gr.DataFrame(
1708
+ headers=["Metric", "Value"],
1709
+ datatype=["str", "number"],
1710
+ interactive=False
1711
+ )
1712
+
1713
+ with gr.TabItem("Habits"):
1714
+ gr.Markdown("### Daily Habits Tracking")
1715
+ habits_view = gr.DataFrame(
1716
+ headers=["Habit", "M", "T", "W", "Th", "F", "S", "Su"],
1717
+ datatype=["str", "str", "str", "str", "str", "str", "str", "str"],
1718
+ interactive=False,
1719
+ value=[[habit] + ["×"] * 7 for habit in load_habits()] # Initialize with default values
1720
+ )
1721
+
1722
+ with gr.TabItem("Journal"):
1723
+ journal_view = gr.DataFrame(
1724
+ headers=["Question", "Response"],
1725
+ datatype=["str", "str"],
1726
+ interactive=False
1727
+ )
1728
+
1729
+ with gr.TabItem("Focus & Meals"):
1730
+ focus_meals_md = gr.Markdown()
1731
+
1732
+ # Connect event handlers
1733
+ history_table.select(
1734
+ fn=load_day_details,
1735
+ outputs=[
1736
+ selected_date,
1737
+ metrics_view,
1738
+ habits_view,
1739
+ journal_view,
1740
+ focus_meals_md
1741
+ ]
1742
+ )
1743
+
1744
+ refresh_btn.click(
1745
+ fn=load_history_data,
1746
+ outputs=[history_table]
1747
+ )
1748
+
1749
+ # Documentation Tab
1750
+ with gr.TabItem("Documentation"):
1751
+ with gr.Column():
1752
+ gr.Markdown("""
1753
+ # Potential Made Simple System Documentation
1754
+
1755
+ ## Overview
1756
+ Welcome to your Life Tracking System, inspired by Rob Dyrdek's "Rhythm of Existence" philosophy. This comprehensive app helps you monitor and optimize various aspects of your life, creating a harmonious balance between work, health, personal life, and sleep.
1757
+
1758
+ ## Philosophy
1759
+ The "Rhythm of Existence" philosophy, pioneered by entrepreneur Rob Dyrdek, emphasizes the importance of creating intentional, balanced routines that maximize both productivity and life satisfaction. Key principles include:
1760
+
1761
+ - **Intentional Living**: Every day is an opportunity to make progress towards your goals
1762
+ - **Balance**: Maintaining equilibrium between work, health, relationships, and personal growth
1763
+ - **Consistency**: Building sustainable habits that compound over time
1764
+ - **Measurement**: What gets measured gets improved
1765
+ - **Reflection**: Regular review and adjustment of life patterns
1766
+
1767
+ ## Features
1768
+
1769
+ ### 1. Must Do Daily
1770
+ - Track daily habits and routines
1771
+ - Organize tasks into morning, evening, and flexible time slots
1772
+ - Monitor weekly progress and completion rates
1773
+ - Set custom durations for time-based activities
1774
+
1775
+ ### 2. Today's Focus
1776
+ - Prioritize tasks with "Must Do Today" and "For Later" lists
1777
+ - Set rewards for completing priority tasks
1778
+ - Track task status and progress
1779
+ - Maintain clarity on daily objectives
1780
+
1781
+ ### 3. Meals
1782
+ - Log daily meals and snacks
1783
+ - Monitor eating patterns
1784
+ - Track nutritional consistency
1785
+
1786
+ ### 4. Life Overview
1787
+ - Calculate and visualize your life journey
1788
+ - Track progress through different life seasons
1789
+ - Set and monitor life milestones
1790
+ - Understand time allocation and remaining potential
1791
+
1792
+ ### 5. Progress Tracking
1793
+ - Monitor daily metrics:
1794
+ - Productivity (0-5)
1795
+ - Energy Level (0-5)
1796
+ - Mood (0-5)
1797
+ - Sleep Quality (0-5)
1798
+ - Exercise Intensity (0-5)
1799
+ - View weekly and monthly trends
1800
+ - Track success rates and improvements
1801
+
1802
+ ### 6. Daily Journal
1803
+ - Reflect on daily experiences
1804
+ - Answer guided questions for deeper insight
1805
+ - Export journal entries for review
1806
+ - Build self-awareness through consistent reflection
1807
+
1808
+ ### 7. History
1809
+ - Review past data and trends
1810
+ - Analyze patterns in habits and metrics
1811
+ - Track long-term progress
1812
+ - Learn from historical patterns
1813
+
1814
+ ## Best Practices
1815
+
1816
+ 1. **Morning Routine**
1817
+ - Start with mindfulness and exercise
1818
+ - Review daily priorities
1819
+ - Set intentions for the day
1820
+
1821
+ 2. **Throughout the Day**
1822
+ - Update task status regularly
1823
+ - Log meals as they happen
1824
+ - Track metrics while they're fresh
1825
+
1826
+ 3. **Evening Routine**
1827
+ - Complete journal entries
1828
+ - Review task completion
1829
+ - Plan for tomorrow
1830
+
1831
+ 4. **Weekly Review**
1832
+ - Analyze progress trends
1833
+ - Adjust habits as needed
1834
+ - Set new goals and rewards
1835
+
1836
+ ## Tips for Success
1837
+
1838
+ 1. **Start Small**
1839
+ - Begin with a few key habits
1840
+ - Gradually add more as you build consistency
1841
+ - Focus on progress, not perfection
1842
+
1843
+ 2. **Be Consistent**
1844
+ - Log data daily
1845
+ - Complete journal entries regularly
1846
+ - Track metrics consistently
1847
+
1848
+ 3. **Review and Adjust**
1849
+ - Use the History tab to spot patterns
1850
+ - Adjust goals based on progress
1851
+ - Celebrate improvements
1852
+
1853
+ 4. **Stay Motivated**
1854
+ - Set meaningful rewards
1855
+ - Track progress visually
1856
+ - Share achievements with others
1857
+
1858
+ ## Technical Notes
1859
+
1860
+ - Data is saved automatically
1861
+ - Manual save option available
1862
+ - Export functionality for journal entries
1863
+ - Daily refresh at midnight
1864
+ - Ability to view and edit past dates
1865
+
1866
+ ## Support and Feedback
1867
+
1868
+ This is a free tool designed to help you optimize your life and create your own perfect rhythm of existence. As you use the app, remember that the goal is progress, not perfection. Every small improvement compounds over time to create significant life changes.
1869
+
1870
+ For support or to share feedback, please visit the project's repository or contact the development team.
1871
+
1872
+ Happy tracking! 🌟
1873
+ """)
1874
+
1875
+ # Date selection handlers
1876
+ def load_selected_date(date):
1877
+ try:
1878
+ # Validate date format
1879
+ datetime.datetime.strptime(date, "%Y-%m-%d")
1880
+ data = load_daily_data(date)
1881
+
1882
+ # Update display date
1883
+ display_date = datetime.datetime.strptime(date, "%Y-%m-%d").strftime("%A, %B %d")
1884
+
1885
+ # Update journal entries if they exist
1886
+ journal_updates = []
1887
+ for question in JOURNAL_QUESTIONS:
1888
+ journal_updates.append(data.get("journal", {}).get(question, ""))
1889
+
1890
+ return [display_date] + journal_updates
1891
+ except ValueError:
1892
+ return [date_text.value] + ["" for _ in JOURNAL_QUESTIONS]
1893
+
1894
+ def return_to_today():
1895
+ today = get_date_key()
1896
+ date_picker.value = today
1897
+ return [today, datetime.datetime.now().strftime("%A, %B %d")]
1898
+
1899
+ # Connect date selection events
1900
+ date_picker.change(
1901
+ fn=load_selected_date,
1902
+ inputs=[date_picker],
1903
+ outputs=[date_text] + list(journal_inputs.values())
1904
+ )
1905
+
1906
+ today_btn.click(
1907
+ fn=return_to_today,
1908
+ outputs=[date_picker, date_text]
1909
+ )
1910
+
1911
+ # Auto-refresh every minute
1912
+ def auto_refresh():
1913
+ """Auto refresh function to check and refresh day data"""
1914
+ try:
1915
+ current_data = load_daily_data()
1916
+ current_data = check_and_refresh_day(current_data)
1917
+ save_daily_data(current_data)
1918
+ except Exception as e:
1919
+ print(f"Error in auto refresh: {e}")
1920
+
1921
+ demo.load(fn=auto_refresh) # No outputs needed
1922
+ demo.queue()
1923
+
1924
+ # Connect the date selection events
1925
+ date_picker.change(
1926
+ fn=load_selected_date_metrics,
1927
+ inputs=[date_picker],
1928
+ outputs=[productivity, energy, mood, sleep, exercise]
1929
+ )
1930
+
1931
+ refresh_dates_btn.click(
1932
+ fn=refresh_dates,
1933
+ outputs=[date_picker]
1934
+ )
1935
+
1936
+ demo.launch()
logo.png ADDED

Git LFS Details

  • SHA256: 67fa0380a40f31b4ec105ad0d3d212311ea7060648bf7a8b6ceef2ec5a087a09
  • Pointer size: 132 Bytes
  • Size of remote file: 1.63 MB
requirements.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ gradio
2
+ requests
3
+ openai
4
+ plotly