Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -4,677 +4,416 @@ import numpy as np
|
|
| 4 |
import matplotlib.pyplot as plt
|
| 5 |
import os
|
| 6 |
import datetime
|
|
|
|
| 7 |
from sklearn.linear_model import LinearRegression
|
| 8 |
from sklearn.preprocessing import StandardScaler
|
| 9 |
import joblib
|
| 10 |
import warnings
|
| 11 |
warnings.filterwarnings('ignore')
|
| 12 |
|
| 13 |
-
# Initialize data directory and
|
| 14 |
os.makedirs('data', exist_ok=True)
|
| 15 |
DATA_FILE = 'data/weight_journal.csv'
|
|
|
|
| 16 |
MODEL_FILE = 'data/weight_prediction_model.pkl'
|
| 17 |
SCALER_FILE = 'data/weight_prediction_scaler.pkl'
|
|
|
|
| 18 |
|
| 19 |
-
#
|
| 20 |
if os.path.exists(DATA_FILE):
|
| 21 |
df = pd.read_csv(DATA_FILE)
|
| 22 |
-
df['date'] = pd.to_datetime(df['date'])
|
| 23 |
else:
|
| 24 |
df = pd.DataFrame(columns=['date', 'weight', 'calories_consumed', 'calories_burned',
|
| 25 |
'water_intake', 'sleep_hours', 'stress_level', 'mood',
|
| 26 |
'exercise_minutes', 'notes'])
|
| 27 |
df.to_csv(DATA_FILE, index=False)
|
| 28 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 29 |
def add_entry(date, weight, calories_consumed, calories_burned, water_intake, sleep_hours,
|
| 30 |
stress_level, mood, exercise_minutes, notes):
|
| 31 |
-
"""Add a new entry to the journal"""
|
| 32 |
global df
|
| 33 |
-
|
| 34 |
-
# Validate required fields
|
| 35 |
if not date or not weight:
|
| 36 |
return "❌ Date and weight are required fields."
|
| 37 |
-
|
| 38 |
try:
|
| 39 |
-
# Convert date to datetime
|
| 40 |
entry_date = pd.to_datetime(date)
|
| 41 |
-
|
| 42 |
-
# Check if an entry with this date already exists
|
| 43 |
if entry_date in df['date'].values:
|
| 44 |
-
return f"❌ An entry for {date} already exists.
|
| 45 |
-
|
| 46 |
-
# Create new entry
|
| 47 |
new_entry = {
|
| 48 |
'date': entry_date,
|
| 49 |
-
'weight': weight,
|
| 50 |
-
'calories_consumed': calories_consumed
|
| 51 |
-
'calories_burned': calories_burned
|
| 52 |
-
'water_intake': water_intake
|
| 53 |
-
'sleep_hours': sleep_hours
|
| 54 |
-
'stress_level': stress_level
|
| 55 |
-
'mood': mood
|
| 56 |
-
'exercise_minutes': exercise_minutes
|
| 57 |
-
'notes': notes
|
| 58 |
}
|
| 59 |
-
|
| 60 |
-
# Add to dataframe
|
| 61 |
df = pd.concat([df, pd.DataFrame([new_entry])], ignore_index=True)
|
| 62 |
-
df = df.sort_values('date', ascending=False)
|
| 63 |
-
|
| 64 |
-
# Save to CSV
|
| 65 |
df.to_csv(DATA_FILE, index=False)
|
| 66 |
-
|
| 67 |
-
# Run prediction update if we have enough data
|
| 68 |
-
if len(df) >= 7: # Need at least a week of data for prediction
|
| 69 |
-
train_prediction_model()
|
| 70 |
-
|
| 71 |
return f"✅ Entry for {date} added successfully!"
|
| 72 |
-
|
| 73 |
except Exception as e:
|
| 74 |
return f"❌ Error: {str(e)}"
|
| 75 |
|
| 76 |
def view_entries(num_entries=10):
|
| 77 |
-
"""View most recent entries in the journal"""
|
| 78 |
-
global df
|
| 79 |
-
|
| 80 |
if len(df) == 0:
|
| 81 |
-
return pd.DataFrame(columns=
|
| 82 |
-
|
| 83 |
-
'exercise_minutes', 'notes'])
|
| 84 |
-
|
| 85 |
-
# Sort by date and get most recent entries
|
| 86 |
-
sorted_df = df.sort_values('date', ascending=False)
|
| 87 |
-
|
| 88 |
-
# Format date for display
|
| 89 |
-
display_df = sorted_df.head(int(num_entries)).copy()
|
| 90 |
display_df['date'] = display_df['date'].dt.strftime('%Y-%m-%d')
|
| 91 |
-
|
| 92 |
return display_df
|
| 93 |
|
| 94 |
def update_entry(date, weight, calories_consumed, calories_burned, water_intake, sleep_hours,
|
| 95 |
-
|
| 96 |
-
"""Update an existing entry in the journal"""
|
| 97 |
global df
|
| 98 |
-
|
| 99 |
-
# Validate date
|
| 100 |
if not date:
|
| 101 |
return "❌ Date is required."
|
| 102 |
-
|
| 103 |
try:
|
| 104 |
-
# Convert date to datetime
|
| 105 |
entry_date = pd.to_datetime(date)
|
| 106 |
-
|
| 107 |
-
# Check if an entry with this date exists
|
| 108 |
if entry_date not in df['date'].values:
|
| 109 |
return f"❌ No entry found for {date}."
|
| 110 |
-
|
| 111 |
-
# Get the row index
|
| 112 |
idx = df[df['date'] == entry_date].index[0]
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
if
|
| 116 |
-
|
| 117 |
-
if
|
| 118 |
-
|
| 119 |
-
if
|
| 120 |
-
|
| 121 |
-
if
|
| 122 |
-
df.at[idx, 'water_intake'] = water_intake
|
| 123 |
-
if sleep_hours is not None:
|
| 124 |
-
df.at[idx, 'sleep_hours'] = sleep_hours
|
| 125 |
-
if stress_level is not None:
|
| 126 |
-
df.at[idx, 'stress_level'] = stress_level
|
| 127 |
-
if mood:
|
| 128 |
-
df.at[idx, 'mood'] = mood
|
| 129 |
-
if exercise_minutes is not None:
|
| 130 |
-
df.at[idx, 'exercise_minutes'] = exercise_minutes
|
| 131 |
-
if notes is not None:
|
| 132 |
-
df.at[idx, 'notes'] = notes
|
| 133 |
-
|
| 134 |
-
# Save to CSV
|
| 135 |
df.to_csv(DATA_FILE, index=False)
|
| 136 |
-
|
| 137 |
-
# Run prediction update if we have enough data
|
| 138 |
-
if len(df) >= 7: # Need at least a week of data for prediction
|
| 139 |
-
train_prediction_model()
|
| 140 |
-
|
| 141 |
return f"✅ Entry for {date} updated successfully!"
|
| 142 |
-
|
| 143 |
except Exception as e:
|
| 144 |
return f"❌ Error: {str(e)}"
|
| 145 |
|
| 146 |
def delete_entry(date):
|
| 147 |
-
"""Delete an entry from the journal"""
|
| 148 |
global df
|
| 149 |
-
|
| 150 |
-
# Validate date
|
| 151 |
if not date:
|
| 152 |
return "❌ Date is required."
|
| 153 |
-
|
| 154 |
try:
|
| 155 |
-
# Convert date to datetime
|
| 156 |
entry_date = pd.to_datetime(date)
|
| 157 |
-
|
| 158 |
-
# Check if an entry with this date exists
|
| 159 |
if entry_date not in df['date'].values:
|
| 160 |
return f"❌ No entry found for {date}."
|
| 161 |
-
|
| 162 |
-
# Delete the entry
|
| 163 |
df = df[df['date'] != entry_date]
|
| 164 |
-
|
| 165 |
-
# Save to CSV
|
| 166 |
df.to_csv(DATA_FILE, index=False)
|
| 167 |
-
|
| 168 |
-
# Run prediction update if we have enough data
|
| 169 |
-
if len(df) >= 7: # Need at least a week of data for prediction
|
| 170 |
-
train_prediction_model()
|
| 171 |
-
|
| 172 |
return f"✅ Entry for {date} deleted successfully!"
|
| 173 |
-
|
| 174 |
except Exception as e:
|
| 175 |
return f"❌ Error: {str(e)}"
|
| 176 |
|
| 177 |
def load_entry_by_date(date):
|
| 178 |
-
"""Load an entry by date for viewing or editing"""
|
| 179 |
-
global df
|
| 180 |
-
|
| 181 |
-
# Validate date
|
| 182 |
if not date:
|
| 183 |
-
return None
|
| 184 |
-
|
| 185 |
try:
|
| 186 |
-
# Convert date to datetime
|
| 187 |
entry_date = pd.to_datetime(date)
|
| 188 |
-
|
| 189 |
-
# Check if an entry with this date exists
|
| 190 |
if entry_date not in df['date'].values:
|
| 191 |
-
return None
|
| 192 |
-
|
| 193 |
-
# Get the entry
|
| 194 |
entry = df[df['date'] == entry_date].iloc[0]
|
| 195 |
-
|
| 196 |
-
|
| 197 |
-
|
| 198 |
-
entry['water_intake'], entry['sleep_hours'], entry['stress_level'],
|
| 199 |
-
entry['mood'], entry['exercise_minutes'], entry['notes'],
|
| 200 |
-
f"✅ Entry for {date} loaded successfully!")
|
| 201 |
-
|
| 202 |
except Exception as e:
|
| 203 |
-
return None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 204 |
|
| 205 |
def create_weight_plot():
|
| 206 |
-
|
| 207 |
-
|
| 208 |
-
|
| 209 |
-
if len(df) < 3: # Need at least a few entries for a meaningful plot
|
| 210 |
-
return "❌ Not enough data to create a plot. Please add more entries.", None
|
| 211 |
-
|
| 212 |
-
# Create a copy of the dataframe for plotting
|
| 213 |
-
plot_df = df.copy()
|
| 214 |
-
plot_df = plot_df.sort_values('date') # Sort by date for proper timeline
|
| 215 |
-
|
| 216 |
-
# Create the figure
|
| 217 |
plt.figure(figsize=(10, 6))
|
| 218 |
-
plt.plot(plot_df['date'], plot_df['weight'], marker='o', linestyle='-', color='blue')
|
|
|
|
|
|
|
| 219 |
plt.title('Weight Over Time')
|
| 220 |
plt.xlabel('Date')
|
| 221 |
plt.ylabel('Weight')
|
|
|
|
| 222 |
plt.grid(True, alpha=0.3)
|
| 223 |
plt.xticks(rotation=45)
|
| 224 |
-
|
| 225 |
-
# Add a trend line if we have enough data
|
| 226 |
-
if len(plot_df) >= 5:
|
| 227 |
-
x = np.arange(len(plot_df))
|
| 228 |
-
y = plot_df['weight'].values
|
| 229 |
-
z = np.polyfit(x, y, 1)
|
| 230 |
-
p = np.poly1d(z)
|
| 231 |
-
plt.plot(plot_df['date'], p(x), "r--", alpha=0.8, label=f"Trend: {z[0]:.2f} per day")
|
| 232 |
-
plt.legend()
|
| 233 |
-
|
| 234 |
-
# Save the figure to a temporary file
|
| 235 |
temp_file = "temp_weight_plot.png"
|
| 236 |
plt.tight_layout()
|
| 237 |
plt.savefig(temp_file)
|
| 238 |
plt.close()
|
|
|
|
| 239 |
|
| 240 |
-
|
| 241 |
-
|
| 242 |
-
|
| 243 |
-
|
| 244 |
-
|
| 245 |
-
|
| 246 |
-
|
| 247 |
-
|
| 248 |
-
|
| 249 |
-
|
| 250 |
-
|
| 251 |
-
|
| 252 |
-
|
| 253 |
-
# Calculate net calories (consumed - burned)
|
| 254 |
-
plot_df['net_calories'] = plot_df['calories_consumed'] - plot_df['calories_burned']
|
| 255 |
|
| 256 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 257 |
plt.figure(figsize=(10, 6))
|
| 258 |
-
plt.
|
| 259 |
-
plt.
|
| 260 |
-
|
| 261 |
-
|
| 262 |
-
plt.title('
|
| 263 |
plt.xlabel('Date')
|
| 264 |
-
plt.ylabel('
|
| 265 |
plt.grid(True, alpha=0.3)
|
| 266 |
plt.xticks(rotation=45)
|
| 267 |
-
plt.axhline(y=0, color='black', linestyle='-', alpha=0.3)
|
| 268 |
plt.legend()
|
| 269 |
-
|
| 270 |
-
|
| 271 |
-
|
| 272 |
-
|
| 273 |
-
|
| 274 |
-
|
| 275 |
-
|
| 276 |
-
return "✅ Plot generated successfully!", temp_file
|
| 277 |
-
|
| 278 |
-
def create_water_sleep_plot():
|
| 279 |
-
"""Create a plot showing water intake vs sleep hours over time"""
|
| 280 |
-
global df
|
| 281 |
-
|
| 282 |
-
if len(df) < 3: # Need at least a few entries for a meaningful plot
|
| 283 |
-
return "❌ Not enough data to create a plot. Please add more entries.", None
|
| 284 |
-
|
| 285 |
-
# Create a copy of the dataframe for plotting
|
| 286 |
-
plot_df = df.copy()
|
| 287 |
-
plot_df = plot_df.sort_values('date') # Sort by date for proper timeline
|
| 288 |
-
|
| 289 |
-
# Create a figure with dual y-axis
|
| 290 |
-
fig, ax1 = plt.subplots(figsize=(10, 6))
|
| 291 |
-
|
| 292 |
-
# Plot water intake on left y-axis
|
| 293 |
-
color = 'tab:blue'
|
| 294 |
-
ax1.set_xlabel('Date')
|
| 295 |
-
ax1.set_ylabel('Water Intake (cups)', color=color)
|
| 296 |
-
ax1.plot(plot_df['date'], plot_df['water_intake'], color=color, marker='o', label='Water Intake')
|
| 297 |
-
ax1.tick_params(axis='y', labelcolor=color)
|
| 298 |
-
|
| 299 |
-
# Create second y-axis for sleep hours
|
| 300 |
-
ax2 = ax1.twinx()
|
| 301 |
-
color = 'tab:red'
|
| 302 |
-
ax2.set_ylabel('Sleep Hours', color=color)
|
| 303 |
-
ax2.plot(plot_df['date'], plot_df['sleep_hours'], color=color, marker='s', label='Sleep Hours')
|
| 304 |
-
ax2.tick_params(axis='y', labelcolor=color)
|
| 305 |
-
|
| 306 |
-
# Rotate date labels for better readability
|
| 307 |
-
fig.autofmt_xdate()
|
| 308 |
-
|
| 309 |
-
# Add title and grid
|
| 310 |
-
plt.title('Water Intake vs Sleep Hours Over Time')
|
| 311 |
-
ax1.grid(True, alpha=0.3)
|
| 312 |
-
|
| 313 |
-
# Add a legend
|
| 314 |
-
lines1, labels1 = ax1.get_legend_handles_labels()
|
| 315 |
-
lines2, labels2 = ax2.get_legend_handles_labels()
|
| 316 |
-
ax1.legend(lines1 + lines2, labels1 + labels2, loc='best')
|
| 317 |
-
|
| 318 |
-
# Save the figure to a temporary file
|
| 319 |
-
temp_file = "temp_water_sleep_plot.png"
|
| 320 |
-
plt.tight_layout()
|
| 321 |
-
plt.savefig(temp_file)
|
| 322 |
-
plt.close()
|
| 323 |
-
|
| 324 |
-
return "✅ Plot generated successfully!", temp_file
|
| 325 |
|
| 326 |
def train_prediction_model():
|
| 327 |
-
|
| 328 |
-
|
| 329 |
-
|
| 330 |
-
|
| 331 |
-
|
| 332 |
-
|
| 333 |
-
|
| 334 |
-
|
| 335 |
-
|
| 336 |
-
|
| 337 |
-
|
| 338 |
-
|
| 339 |
-
|
| 340 |
-
|
| 341 |
-
|
| 342 |
-
|
| 343 |
-
|
| 344 |
-
|
| 345 |
-
|
| 346 |
-
|
| 347 |
-
X[col] = X[col].fillna(X[col].median())
|
| 348 |
-
|
| 349 |
-
# Scale features
|
| 350 |
-
scaler = StandardScaler()
|
| 351 |
-
X_scaled = scaler.fit_transform(X)
|
| 352 |
-
|
| 353 |
-
# Train model
|
| 354 |
-
model = LinearRegression()
|
| 355 |
-
model.fit(X_scaled, y)
|
| 356 |
-
|
| 357 |
-
# Save model and scaler
|
| 358 |
-
joblib.dump(model, MODEL_FILE)
|
| 359 |
-
joblib.dump(scaler, SCALER_FILE)
|
| 360 |
-
|
| 361 |
-
# Calculate model performance
|
| 362 |
-
y_pred = model.predict(X_scaled)
|
| 363 |
-
mse = np.mean((y - y_pred) ** 2)
|
| 364 |
-
rmse = np.sqrt(mse)
|
| 365 |
-
|
| 366 |
-
# Calculate feature importance
|
| 367 |
-
importance = pd.DataFrame({
|
| 368 |
-
'Feature': features,
|
| 369 |
-
'Importance': np.abs(model.coef_)
|
| 370 |
-
}).sort_values('Importance', ascending=False)
|
| 371 |
-
|
| 372 |
-
# Generate report
|
| 373 |
-
report = f"✅ Model trained successfully!\n\n"
|
| 374 |
-
report += f"Model performance: RMSE = {rmse:.2f}\n\n"
|
| 375 |
-
report += "Feature Importance:\n"
|
| 376 |
-
for _, row in importance.iterrows():
|
| 377 |
-
report += f"- {row['Feature']}: {row['Importance']:.2f}\n"
|
| 378 |
-
|
| 379 |
-
return report
|
| 380 |
-
|
| 381 |
-
except Exception as e:
|
| 382 |
-
return f"❌ Error training model: {str(e)}"
|
| 383 |
-
|
| 384 |
-
def predict_weight_trajectory(days=30, calorie_adjustment=0, water_adjustment=0,
|
| 385 |
-
sleep_adjustment=0, stress_adjustment=0, exercise_adjustment=0):
|
| 386 |
-
"""Predict weight trajectory for the next N days based on current trends and adjustments"""
|
| 387 |
-
global df
|
| 388 |
-
|
| 389 |
-
if len(df) < 7: # Need at least a week of data
|
| 390 |
-
return "❌ Not enough data for prediction. Please add at least 7 entries.", None, ""
|
| 391 |
-
|
| 392 |
-
if not os.path.exists(MODEL_FILE) or not os.path.exists(SCALER_FILE):
|
| 393 |
-
return "❌ Prediction model not trained. Please train the model first.", None, ""
|
| 394 |
-
|
| 395 |
-
try:
|
| 396 |
-
# Load model and scaler
|
| 397 |
-
model = joblib.load(MODEL_FILE)
|
| 398 |
-
scaler = joblib.load(SCALER_FILE)
|
| 399 |
-
|
| 400 |
-
# Get the most recent entry as baseline
|
| 401 |
-
last_entry = df.sort_values('date', ascending=False).iloc[0]
|
| 402 |
-
|
| 403 |
-
# Get average values from the last week for prediction
|
| 404 |
-
last_week = df.sort_values('date', ascending=False).head(7)
|
| 405 |
-
avg_calories_consumed = last_week['calories_consumed'].mean() + calorie_adjustment
|
| 406 |
-
avg_calories_burned = last_week['calories_burned'].mean() + calorie_adjustment # We apply same adjustment
|
| 407 |
-
avg_water = last_week['water_intake'].mean() + water_adjustment
|
| 408 |
-
avg_sleep = last_week['sleep_hours'].mean() + sleep_adjustment
|
| 409 |
-
avg_stress = last_week['stress_level'].mean() + stress_adjustment
|
| 410 |
-
avg_exercise = last_week['exercise_minutes'].mean() + exercise_adjustment
|
| 411 |
-
|
| 412 |
-
# Create prediction data
|
| 413 |
-
future_dates = [last_entry['date'] + datetime.timedelta(days=i) for i in range(1, days+1)]
|
| 414 |
-
predict_data = pd.DataFrame({
|
| 415 |
-
'date': future_dates,
|
| 416 |
-
'calories_consumed': [avg_calories_consumed] * days,
|
| 417 |
-
'calories_burned': [avg_calories_burned] * days,
|
| 418 |
-
'water_intake': [avg_water] * days,
|
| 419 |
-
'sleep_hours': [avg_sleep] * days,
|
| 420 |
-
'stress_level': [avg_stress] * days,
|
| 421 |
-
'exercise_minutes': [avg_exercise] * days
|
| 422 |
-
})
|
| 423 |
-
|
| 424 |
-
# Scale features for prediction
|
| 425 |
-
features = ['calories_consumed', 'calories_burned', 'water_intake',
|
| 426 |
-
'sleep_hours', 'stress_level', 'exercise_minutes']
|
| 427 |
-
X_pred = predict_data[features]
|
| 428 |
-
X_pred_scaled = scaler.transform(X_pred)
|
| 429 |
-
|
| 430 |
-
# Predict weights
|
| 431 |
-
predicted_weights = model.predict(X_pred_scaled)
|
| 432 |
-
predict_data['weight'] = predicted_weights
|
| 433 |
-
|
| 434 |
-
# Create plot
|
| 435 |
-
plt.figure(figsize=(10, 6))
|
| 436 |
-
|
| 437 |
-
# Plot historical data
|
| 438 |
-
hist_data = df.copy().sort_values('date')
|
| 439 |
-
plt.plot(hist_data['date'], hist_data['weight'], 'o-', color='blue', label='Historical')
|
| 440 |
-
|
| 441 |
-
# Plot prediction
|
| 442 |
-
plt.plot(predict_data['date'], predict_data['weight'], 'o--', color='red', label='Predicted')
|
| 443 |
-
|
| 444 |
-
# Add details
|
| 445 |
-
plt.title('Weight Trajectory Prediction')
|
| 446 |
-
plt.xlabel('Date')
|
| 447 |
-
plt.ylabel('Weight')
|
| 448 |
-
plt.grid(True, alpha=0.3)
|
| 449 |
-
plt.xticks(rotation=45)
|
| 450 |
-
plt.legend()
|
| 451 |
-
|
| 452 |
-
# Calculate metrics
|
| 453 |
-
start_weight = hist_data['weight'].iloc[-1]
|
| 454 |
-
end_weight = predict_data['weight'].iloc[-1]
|
| 455 |
-
weight_change = end_weight - start_weight
|
| 456 |
-
|
| 457 |
-
# Generate report
|
| 458 |
-
report = f"✅ Prediction completed!\n\n"
|
| 459 |
-
report += f"Starting weight: {start_weight:.1f}\n"
|
| 460 |
-
report += f"Predicted weight after {days} days: {end_weight:.1f}\n"
|
| 461 |
-
report += f"Projected change: {weight_change:.1f} ({weight_change/start_weight*100:.1f}%)\n"
|
| 462 |
-
|
| 463 |
-
# Generate recommendations
|
| 464 |
-
if weight_change > 0:
|
| 465 |
-
recommendations = "## 📊 Recommendations for Weight Loss:\n\n"
|
| 466 |
-
recommendations += "Based on the prediction model, here are personalized recommendations:\n\n"
|
| 467 |
-
|
| 468 |
-
if calorie_adjustment >= 0:
|
| 469 |
-
recommendations += "1. **Reduce calorie intake** by 300-500 calories per day\n"
|
| 470 |
-
if avg_exercise < 30:
|
| 471 |
-
recommendations += "2. **Increase exercise** to at least 30 minutes per day\n"
|
| 472 |
-
if avg_water < 8:
|
| 473 |
-
recommendations += "3. **Drink more water**, aim for 8+ cups daily\n"
|
| 474 |
-
if avg_sleep < 7:
|
| 475 |
-
recommendations += "4. **Improve sleep quality** to 7-8 hours per night\n"
|
| 476 |
-
if avg_stress > 5:
|
| 477 |
-
recommendations += "5. **Implement stress management techniques** like meditation\n"
|
| 478 |
-
else:
|
| 479 |
-
recommendations = "## 📊 Recommendations to Maintain Progress:\n\n"
|
| 480 |
-
recommendations += "Based on the prediction model, you're on track! Here are tips to maintain progress:\n\n"
|
| 481 |
-
recommendations += "1. **Keep consistent** with your current habits\n"
|
| 482 |
-
recommendations += "2. **Add variety** to your exercise routine to stay engaged\n"
|
| 483 |
-
recommendations += "3. **Continue tracking** your metrics to monitor trends\n"
|
| 484 |
-
|
| 485 |
-
return report, plt, recommendations
|
| 486 |
-
|
| 487 |
-
except Exception as e:
|
| 488 |
-
return f"❌ Error generating prediction: {str(e)}", None, ""
|
| 489 |
-
|
| 490 |
-
def create_sample_data():
|
| 491 |
-
"""Create sample data for demonstration purposes"""
|
| 492 |
-
# Start date (30 days ago)
|
| 493 |
-
start_date = datetime.datetime.now() - datetime.timedelta(days=30)
|
| 494 |
-
|
| 495 |
-
# Initialize weight and other parameters
|
| 496 |
-
initial_weight = 180 # Example starting weight
|
| 497 |
-
|
| 498 |
-
# Create sample dataframe
|
| 499 |
-
sample_data = []
|
| 500 |
-
|
| 501 |
-
for i in range(30):
|
| 502 |
-
# Current date
|
| 503 |
-
current_date = start_date + datetime.timedelta(days=i)
|
| 504 |
-
|
| 505 |
-
# Weight trend (gradually decreasing with some fluctuation)
|
| 506 |
-
# Add some randomness for realism
|
| 507 |
-
weight_change = -0.1 * i / 30 # Gradual decrease
|
| 508 |
-
daily_fluctuation = np.random.normal(0, 0.3) # Random daily fluctuation
|
| 509 |
-
current_weight = initial_weight + (weight_change * initial_weight) + daily_fluctuation
|
| 510 |
-
|
| 511 |
-
# Other parameters (with realistic values and some correlation to weight loss)
|
| 512 |
-
calories_consumed = np.random.randint(1800, 2300)
|
| 513 |
-
calories_burned = np.random.randint(2000, 2500)
|
| 514 |
-
water_intake = np.random.randint(5, 10)
|
| 515 |
-
sleep_hours = np.random.normal(7, 0.5)
|
| 516 |
-
stress_level = np.random.randint(3, 8)
|
| 517 |
-
exercise_minutes = np.random.randint(20, 60)
|
| 518 |
-
|
| 519 |
-
# Mood (correlated somewhat with other factors)
|
| 520 |
-
mood_options = ['Great', 'Good', 'Neutral', 'Poor']
|
| 521 |
-
mood_probs = [0.2, 0.5, 0.2, 0.1] # Probability distribution
|
| 522 |
-
mood = np.random.choice(mood_options, p=mood_probs)
|
| 523 |
-
|
| 524 |
-
# Notes
|
| 525 |
-
notes = ''
|
| 526 |
-
|
| 527 |
-
# Add to sample data
|
| 528 |
-
sample_data.append({
|
| 529 |
-
'date': current_date,
|
| 530 |
-
'weight': round(current_weight, 1),
|
| 531 |
-
'calories_consumed': calories_consumed,
|
| 532 |
-
'calories_burned': calories_burned,
|
| 533 |
-
'water_intake': water_intake,
|
| 534 |
-
'sleep_hours': round(sleep_hours, 1),
|
| 535 |
-
'stress_level': stress_level,
|
| 536 |
-
'mood': mood,
|
| 537 |
-
'exercise_minutes': exercise_minutes,
|
| 538 |
-
'notes': notes
|
| 539 |
-
})
|
| 540 |
-
|
| 541 |
-
return pd.DataFrame(sample_data)
|
| 542 |
|
| 543 |
# Gradio Interface
|
| 544 |
-
with gr.Blocks(title="
|
| 545 |
-
gr.Markdown("# 🏋️♀️ Weight Loss
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 546 |
|
| 547 |
with gr.Tab("Add Entry"):
|
| 548 |
with gr.Row():
|
| 549 |
with gr.Column():
|
| 550 |
date_input = gr.Textbox(label="Date (YYYY-MM-DD)")
|
| 551 |
-
weight_input = gr.Number(label="Weight")
|
| 552 |
calories_consumed_input = gr.Number(label="Calories Consumed")
|
| 553 |
calories_burned_input = gr.Number(label="Calories Burned")
|
| 554 |
water_intake_input = gr.Number(label="Water Intake (cups)")
|
| 555 |
sleep_hours_input = gr.Number(label="Sleep Hours")
|
| 556 |
-
stress_level_input = gr.Slider(
|
| 557 |
-
mood_input = gr.Dropdown(
|
| 558 |
exercise_minutes_input = gr.Number(label="Exercise Minutes")
|
| 559 |
notes_input = gr.Textbox(label="Notes")
|
| 560 |
add_button = gr.Button("Add Entry", variant="primary")
|
| 561 |
add_output = gr.Markdown()
|
| 562 |
-
add_button.click(add_entry,
|
| 563 |
-
|
| 564 |
-
|
| 565 |
-
exercise_minutes_input, notes_input],
|
| 566 |
-
outputs=add_output)
|
| 567 |
|
| 568 |
with gr.Tab("View Entries"):
|
| 569 |
-
num_entries_view = gr.Slider(
|
| 570 |
-
view_button = gr.Button("View
|
| 571 |
view_output = gr.DataFrame()
|
| 572 |
view_button.click(view_entries, inputs=num_entries_view, outputs=view_output)
|
| 573 |
|
| 574 |
with gr.Tab("Update Entry"):
|
| 575 |
-
date_update_input = gr.Textbox(label="Date
|
| 576 |
with gr.Row():
|
| 577 |
with gr.Column():
|
| 578 |
-
weight_update_input = gr.Number(label="Weight")
|
| 579 |
calories_consumed_update_input = gr.Number(label="Calories Consumed")
|
| 580 |
calories_burned_update_input = gr.Number(label="Calories Burned")
|
| 581 |
water_intake_update_input = gr.Number(label="Water Intake (cups)")
|
| 582 |
sleep_hours_update_input = gr.Number(label="Sleep Hours")
|
| 583 |
-
stress_level_update_input = gr.Slider(
|
| 584 |
-
mood_update_input = gr.Dropdown(
|
| 585 |
exercise_minutes_update_input = gr.Number(label="Exercise Minutes")
|
| 586 |
notes_update_input = gr.Textbox(label="Notes")
|
| 587 |
-
update_button = gr.Button("Update
|
| 588 |
update_output = gr.Markdown()
|
| 589 |
-
update_button.click(update_entry,
|
| 590 |
-
|
| 591 |
-
|
| 592 |
-
|
| 593 |
-
|
| 594 |
-
|
| 595 |
-
|
| 596 |
-
|
| 597 |
-
|
| 598 |
-
|
| 599 |
-
|
| 600 |
-
|
| 601 |
-
|
| 602 |
-
|
| 603 |
-
|
| 604 |
-
|
| 605 |
-
|
| 606 |
-
|
| 607 |
-
weight_load_output = gr.Number(label="Weight")
|
| 608 |
-
calories_consumed_load_output = gr.Number(label="Calories Consumed")
|
| 609 |
-
calories_burned_load_output = gr.Number(label="Calories Burned")
|
| 610 |
-
water_intake_load_output = gr.Number(label="Water Intake (cups)")
|
| 611 |
-
sleep_hours_load_output = gr.Number(label="Sleep Hours")
|
| 612 |
-
stress_level_load_output = gr.Slider(minimum=1, maximum=10, step=1, label="Stress Level (1-10)")
|
| 613 |
-
mood_load_output = gr.Dropdown(choices=['Great', 'Good', 'Neutral', 'Poor'], label="Mood")
|
| 614 |
-
exercise_minutes_load_output = gr.Number(label="Exercise Minutes")
|
| 615 |
-
notes_load_output = gr.Textbox(label="Notes")
|
| 616 |
-
load_output = gr.Markdown()
|
| 617 |
-
load_button.click(load_entry_by_date, inputs=date_load_input, outputs=[weight_load_output, calories_consumed_load_output, calories_burned_load_output, water_intake_load_output, sleep_hours_load_output, stress_level_load_output, mood_load_output, exercise_minutes_load_output, notes_load_output, load_output])
|
| 618 |
|
| 619 |
with gr.Tab("Analytics"):
|
| 620 |
-
|
| 621 |
-
|
| 622 |
-
|
| 623 |
-
|
| 624 |
-
weight_plot_output = gr.Image()
|
| 625 |
-
with gr.Column():
|
| 626 |
-
calories_plot_button = gr.Button("Calories Consumed vs. Burned", variant="secondary")
|
| 627 |
-
calories_plot_output_text = gr.Markdown()
|
| 628 |
-
calories_plot_output = gr.Image()
|
| 629 |
-
with gr.Column():
|
| 630 |
-
water_sleep_plot_button = gr.Button("Water Intake vs. Sleep Hours", variant="secondary")
|
| 631 |
-
water_sleep_plot_output_text = gr.Markdown()
|
| 632 |
-
water_sleep_plot_output = gr.Image()
|
| 633 |
-
|
| 634 |
-
weight_plot_button.click(create_weight_plot, inputs=[], outputs=[weight_plot_output_text, weight_plot_output])
|
| 635 |
-
calories_plot_button.click(create_calories_plot, inputs=[], outputs=[calories_plot_output_text, calories_plot_output])
|
| 636 |
-
water_sleep_plot_button.click(create_water_sleep_plot, inputs=[], outputs=[water_sleep_plot_output_text, water_sleep_plot_output])
|
| 637 |
|
| 638 |
with gr.Tab("Prediction"):
|
| 639 |
-
|
| 640 |
-
|
| 641 |
-
|
| 642 |
-
|
| 643 |
-
water_adjustment_input = gr.Number(label="Water Intake Adjustment")
|
| 644 |
-
sleep_adjustment_input = gr.Number(label="Sleep Hours Adjustment")
|
| 645 |
-
stress_adjustment_input = gr.Number(label="Stress Level Adjustment")
|
| 646 |
-
exercise_adjustment_input = gr.Number(label="Exercise Minutes Adjustment")
|
| 647 |
-
predict_button = gr.Button("Predict Weight Trajectory", variant="primary")
|
| 648 |
prediction_output_text = gr.Markdown()
|
| 649 |
prediction_plot_output = gr.Plot()
|
| 650 |
recommendation_output = gr.Markdown()
|
| 651 |
-
predict_button.click(predict_weight_trajectory,
|
| 652 |
-
inputs=[days_input, calorie_adjustment_input, water_adjustment_input,
|
| 653 |
-
sleep_adjustment_input, stress_adjustment_input, exercise_adjustment_input],
|
| 654 |
outputs=[prediction_output_text, prediction_plot_output, recommendation_output])
|
| 655 |
|
| 656 |
with gr.Tab("Train Model"):
|
| 657 |
-
train_button = gr.Button("Train
|
| 658 |
train_output = gr.Markdown()
|
| 659 |
-
train_button.click(train_prediction_model,
|
| 660 |
|
| 661 |
-
with gr.Tab("
|
| 662 |
-
|
| 663 |
-
|
|
|
|
|
|
|
|
|
|
| 664 |
|
|
|
|
| 665 |
def load_sample_data():
|
| 666 |
-
"""Load sample data into the dataframe"""
|
| 667 |
global df
|
| 668 |
-
|
| 669 |
-
|
| 670 |
-
|
| 671 |
-
|
| 672 |
-
|
| 673 |
-
|
| 674 |
-
|
| 675 |
-
|
| 676 |
-
|
| 677 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 678 |
|
| 679 |
-
sample_button.click(load_sample_data, inputs=[], outputs=sample_output)
|
| 680 |
iface.launch(debug=False)
|
|
|
|
| 4 |
import matplotlib.pyplot as plt
|
| 5 |
import os
|
| 6 |
import datetime
|
| 7 |
+
import json
|
| 8 |
from sklearn.linear_model import LinearRegression
|
| 9 |
from sklearn.preprocessing import StandardScaler
|
| 10 |
import joblib
|
| 11 |
import warnings
|
| 12 |
warnings.filterwarnings('ignore')
|
| 13 |
|
| 14 |
+
# Initialize data directory and files
|
| 15 |
os.makedirs('data', exist_ok=True)
|
| 16 |
DATA_FILE = 'data/weight_journal.csv'
|
| 17 |
+
GOAL_FILE = 'data/goal.json'
|
| 18 |
MODEL_FILE = 'data/weight_prediction_model.pkl'
|
| 19 |
SCALER_FILE = 'data/weight_prediction_scaler.pkl'
|
| 20 |
+
ACHIEVEMENTS_FILE = 'data/achievements.json'
|
| 21 |
|
| 22 |
+
# Load or initialize dataframe
|
| 23 |
if os.path.exists(DATA_FILE):
|
| 24 |
df = pd.read_csv(DATA_FILE)
|
| 25 |
+
df['date'] = pd.to_datetime(df['date'])
|
| 26 |
else:
|
| 27 |
df = pd.DataFrame(columns=['date', 'weight', 'calories_consumed', 'calories_burned',
|
| 28 |
'water_intake', 'sleep_hours', 'stress_level', 'mood',
|
| 29 |
'exercise_minutes', 'notes'])
|
| 30 |
df.to_csv(DATA_FILE, index=False)
|
| 31 |
|
| 32 |
+
# Load or initialize goals
|
| 33 |
+
if os.path.exists(GOAL_FILE):
|
| 34 |
+
with open(GOAL_FILE, 'r') as f:
|
| 35 |
+
goal = json.load(f)
|
| 36 |
+
else:
|
| 37 |
+
goal = {'target_weight': None, 'target_date': None}
|
| 38 |
+
|
| 39 |
+
# Load or initialize achievements
|
| 40 |
+
if os.path.exists(ACHIEVEMENTS_FILE):
|
| 41 |
+
with open(ACHIEVEMENTS_FILE, 'r') as f:
|
| 42 |
+
achievements = json.load(f)
|
| 43 |
+
else:
|
| 44 |
+
achievements = []
|
| 45 |
+
|
| 46 |
+
# Load Indian food dataset with robust column handling
|
| 47 |
+
try:
|
| 48 |
+
from datasets import load_dataset
|
| 49 |
+
ds = load_dataset("Anupam007/nutarian-Indianfood")
|
| 50 |
+
food_df = pd.DataFrame(ds['train'])
|
| 51 |
+
print(f"Successfully loaded Indian food dataset with {len(food_df)} items")
|
| 52 |
+
print("Columns in dataset:", food_df.columns.tolist())
|
| 53 |
+
# Rename potential column names to 'name' for consistency
|
| 54 |
+
if 'name' not in food_df.columns:
|
| 55 |
+
if 'food_name' in food_df.columns:
|
| 56 |
+
food_df = food_df.rename(columns={'food_name': 'name'})
|
| 57 |
+
elif 'item' in food_df.columns:
|
| 58 |
+
food_df = food_df.rename(columns={'item': 'name'})
|
| 59 |
+
else:
|
| 60 |
+
raise ValueError("No suitable 'name' column found in dataset")
|
| 61 |
+
# Ensure 'calories' column exists
|
| 62 |
+
if 'calories' not in food_df.columns and 'calorie_count' in food_df.columns:
|
| 63 |
+
food_df = food_df.rename(columns={'calorie_count': 'calories'})
|
| 64 |
+
elif 'calories' not in food_df.columns:
|
| 65 |
+
raise ValueError("No 'calories' column found in dataset")
|
| 66 |
+
except Exception as e:
|
| 67 |
+
print(f"Failed to load dataset or process columns: {e}. Using fallback data.")
|
| 68 |
+
food_df = pd.DataFrame({
|
| 69 |
+
'name': ['Rice', 'Dal', 'Roti', 'Chicken Curry'],
|
| 70 |
+
'calories': [130, 150, 80, 200]
|
| 71 |
+
})
|
| 72 |
+
|
| 73 |
+
# Core Functions
|
| 74 |
def add_entry(date, weight, calories_consumed, calories_burned, water_intake, sleep_hours,
|
| 75 |
stress_level, mood, exercise_minutes, notes):
|
|
|
|
| 76 |
global df
|
|
|
|
|
|
|
| 77 |
if not date or not weight:
|
| 78 |
return "❌ Date and weight are required fields."
|
|
|
|
| 79 |
try:
|
|
|
|
| 80 |
entry_date = pd.to_datetime(date)
|
|
|
|
|
|
|
| 81 |
if entry_date in df['date'].values:
|
| 82 |
+
return f"❌ An entry for {date} already exists."
|
|
|
|
|
|
|
| 83 |
new_entry = {
|
| 84 |
'date': entry_date,
|
| 85 |
+
'weight': float(weight),
|
| 86 |
+
'calories_consumed': float(calories_consumed or 0),
|
| 87 |
+
'calories_burned': float(calories_burned or 0),
|
| 88 |
+
'water_intake': float(water_intake or 0),
|
| 89 |
+
'sleep_hours': float(sleep_hours or 0),
|
| 90 |
+
'stress_level': int(stress_level or 5),
|
| 91 |
+
'mood': mood or 'Neutral',
|
| 92 |
+
'exercise_minutes': float(exercise_minutes or 0),
|
| 93 |
+
'notes': notes or ''
|
| 94 |
}
|
|
|
|
|
|
|
| 95 |
df = pd.concat([df, pd.DataFrame([new_entry])], ignore_index=True)
|
| 96 |
+
df = df.sort_values('date', ascending=False)
|
|
|
|
|
|
|
| 97 |
df.to_csv(DATA_FILE, index=False)
|
| 98 |
+
update_achievements()
|
|
|
|
|
|
|
|
|
|
|
|
|
| 99 |
return f"✅ Entry for {date} added successfully!"
|
|
|
|
| 100 |
except Exception as e:
|
| 101 |
return f"❌ Error: {str(e)}"
|
| 102 |
|
| 103 |
def view_entries(num_entries=10):
|
|
|
|
|
|
|
|
|
|
| 104 |
if len(df) == 0:
|
| 105 |
+
return pd.DataFrame(columns=df.columns)
|
| 106 |
+
display_df = df.sort_values('date', ascending=False).head(int(num_entries)).copy()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 107 |
display_df['date'] = display_df['date'].dt.strftime('%Y-%m-%d')
|
|
|
|
| 108 |
return display_df
|
| 109 |
|
| 110 |
def update_entry(date, weight, calories_consumed, calories_burned, water_intake, sleep_hours,
|
| 111 |
+
stress_level, mood, exercise_minutes, notes):
|
|
|
|
| 112 |
global df
|
|
|
|
|
|
|
| 113 |
if not date:
|
| 114 |
return "❌ Date is required."
|
|
|
|
| 115 |
try:
|
|
|
|
| 116 |
entry_date = pd.to_datetime(date)
|
|
|
|
|
|
|
| 117 |
if entry_date not in df['date'].values:
|
| 118 |
return f"❌ No entry found for {date}."
|
|
|
|
|
|
|
| 119 |
idx = df[df['date'] == entry_date].index[0]
|
| 120 |
+
if weight is not None: df.at[idx, 'weight'] = float(weight)
|
| 121 |
+
if calories_consumed is not None: df.at[idx, 'calories_consumed'] = float(calories_consumed)
|
| 122 |
+
if calories_burned is not None: df.at[idx, 'calories_burned'] = float(calories_burned)
|
| 123 |
+
if water_intake is not None: df.at[idx, 'water_intake'] = float(water_intake)
|
| 124 |
+
if sleep_hours is not None: df.at[idx, 'sleep_hours'] = float(sleep_hours)
|
| 125 |
+
if stress_level is not None: df.at[idx, 'stress_level'] = int(stress_level)
|
| 126 |
+
if mood: df.at[idx, 'mood'] = mood
|
| 127 |
+
if exercise_minutes is not None: df.at[idx, 'exercise_minutes'] = float(exercise_minutes)
|
| 128 |
+
if notes is not None: df.at[idx, 'notes'] = notes
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 129 |
df.to_csv(DATA_FILE, index=False)
|
| 130 |
+
update_achievements()
|
|
|
|
|
|
|
|
|
|
|
|
|
| 131 |
return f"✅ Entry for {date} updated successfully!"
|
|
|
|
| 132 |
except Exception as e:
|
| 133 |
return f"❌ Error: {str(e)}"
|
| 134 |
|
| 135 |
def delete_entry(date):
|
|
|
|
| 136 |
global df
|
|
|
|
|
|
|
| 137 |
if not date:
|
| 138 |
return "❌ Date is required."
|
|
|
|
| 139 |
try:
|
|
|
|
| 140 |
entry_date = pd.to_datetime(date)
|
|
|
|
|
|
|
| 141 |
if entry_date not in df['date'].values:
|
| 142 |
return f"❌ No entry found for {date}."
|
|
|
|
|
|
|
| 143 |
df = df[df['date'] != entry_date]
|
|
|
|
|
|
|
| 144 |
df.to_csv(DATA_FILE, index=False)
|
| 145 |
+
update_achievements()
|
|
|
|
|
|
|
|
|
|
|
|
|
| 146 |
return f"✅ Entry for {date} deleted successfully!"
|
|
|
|
| 147 |
except Exception as e:
|
| 148 |
return f"❌ Error: {str(e)}"
|
| 149 |
|
| 150 |
def load_entry_by_date(date):
|
|
|
|
|
|
|
|
|
|
|
|
|
| 151 |
if not date:
|
| 152 |
+
return [None] * 9 + ["❌ Date is required."]
|
|
|
|
| 153 |
try:
|
|
|
|
| 154 |
entry_date = pd.to_datetime(date)
|
|
|
|
|
|
|
| 155 |
if entry_date not in df['date'].values:
|
| 156 |
+
return [None] * 9 + [f"❌ No entry found for {date}."]
|
|
|
|
|
|
|
| 157 |
entry = df[df['date'] == entry_date].iloc[0]
|
| 158 |
+
return [entry[col] for col in ['weight', 'calories_consumed', 'calories_burned', 'water_intake',
|
| 159 |
+
'sleep_hours', 'stress_level', 'mood', 'exercise_minutes', 'notes']] + \
|
| 160 |
+
[f"✅ Entry for {date} loaded successfully!"]
|
|
|
|
|
|
|
|
|
|
|
|
|
| 161 |
except Exception as e:
|
| 162 |
+
return [None] * 9 + [f"❌ Error: {str(e)}"]
|
| 163 |
+
|
| 164 |
+
# Visualization Functions
|
| 165 |
+
def create_dashboard():
|
| 166 |
+
if len(df) < 3:
|
| 167 |
+
return "❌ Not enough data.", None
|
| 168 |
+
fig, (ax1, ax2, ax3) = plt.subplots(1, 3, figsize=(15, 5))
|
| 169 |
+
df.sort_values('date').plot(x='date', y='weight', ax=ax1, title="Weight Trend", marker='o', color='blue')
|
| 170 |
+
df.sort_values('date').plot(x='date', y=['calories_consumed', 'calories_burned'], ax=ax2, title="Calories", kind='bar')
|
| 171 |
+
df.sort_values('date').plot(x='date', y='water_intake', ax=ax3, title="Water Intake", marker='s', color='green')
|
| 172 |
+
for ax in [ax1, ax2, ax3]:
|
| 173 |
+
ax.grid(True, alpha=0.3)
|
| 174 |
+
ax.tick_params(axis='x', rotation=45)
|
| 175 |
+
plt.tight_layout()
|
| 176 |
+
return "✅ Dashboard ready!", fig
|
| 177 |
|
| 178 |
def create_weight_plot():
|
| 179 |
+
if len(df) < 3:
|
| 180 |
+
return "❌ Not enough data.", None
|
| 181 |
+
plot_df = df.sort_values('date')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 182 |
plt.figure(figsize=(10, 6))
|
| 183 |
+
plt.plot(plot_df['date'], plot_df['weight'], marker='o', linestyle='-', color='blue', label='Weight')
|
| 184 |
+
if goal['target_weight']:
|
| 185 |
+
plt.axhline(y=goal['target_weight'], color='green', linestyle='--', label='Goal')
|
| 186 |
plt.title('Weight Over Time')
|
| 187 |
plt.xlabel('Date')
|
| 188 |
plt.ylabel('Weight')
|
| 189 |
+
plt.legend()
|
| 190 |
plt.grid(True, alpha=0.3)
|
| 191 |
plt.xticks(rotation=45)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 192 |
temp_file = "temp_weight_plot.png"
|
| 193 |
plt.tight_layout()
|
| 194 |
plt.savefig(temp_file)
|
| 195 |
plt.close()
|
| 196 |
+
return "✅ Plot generated!", temp_file
|
| 197 |
|
| 198 |
+
# Goal Setting
|
| 199 |
+
def set_goal(target_weight, target_date):
|
| 200 |
+
global goal
|
| 201 |
+
if not target_weight or not target_date:
|
| 202 |
+
return "❌ Target weight and date are required."
|
| 203 |
+
try:
|
| 204 |
+
goal = {'target_weight': float(target_weight), 'target_date': pd.to_datetime(target_date).strftime('%Y-%m-%d')}
|
| 205 |
+
with open(GOAL_FILE, 'w') as f:
|
| 206 |
+
json.dump(goal, f)
|
| 207 |
+
return "✅ Goal set successfully!"
|
| 208 |
+
except Exception as e:
|
| 209 |
+
return f"❌ Error: {str(e)}"
|
|
|
|
|
|
|
|
|
|
| 210 |
|
| 211 |
+
def view_goal():
|
| 212 |
+
if not goal['target_weight']:
|
| 213 |
+
return "No goal set yet."
|
| 214 |
+
return f"Target Weight: {goal['target_weight']} | Target Date: {goal['target_date']}\n" + \
|
| 215 |
+
f"Progress: {((df['weight'].iloc[-1] - goal['target_weight']) / (df['weight'].iloc[0] - goal['target_weight']) * 100):.1f}%"
|
| 216 |
+
|
| 217 |
+
# Achievements
|
| 218 |
+
def update_achievements():
|
| 219 |
+
global achievements
|
| 220 |
+
new_achievements = []
|
| 221 |
+
if len(df) >= 1 and "First Entry 🏅" not in achievements:
|
| 222 |
+
new_achievements.append("First Entry 🏅")
|
| 223 |
+
if len(df) >= 7 and df['date'].diff().mean().days <= 1 and "Consistency Star 🌟" not in achievements:
|
| 224 |
+
new_achievements.append("Consistency Star 🌟")
|
| 225 |
+
if len(df) >= 7 and df['water_intake'].tail(7).mean() >= 8 and "Water Warrior 💧" not in achievements:
|
| 226 |
+
new_achievements.append("Water Warrior 💧")
|
| 227 |
+
achievements.extend(new_achievements)
|
| 228 |
+
with open(ACHIEVEMENTS_FILE, 'w') as f:
|
| 229 |
+
json.dump(achievements, f)
|
| 230 |
+
return "\n".join(achievements) or "No achievements yet—keep going!"
|
| 231 |
+
|
| 232 |
+
# Prediction
|
| 233 |
+
def predict_weight_trajectory(days=30, calorie_adjustment=0, exercise_adjustment=0):
|
| 234 |
+
if len(df) < 7 or not os.path.exists(MODEL_FILE):
|
| 235 |
+
return "❌ Not enough data or model not trained.", None, ""
|
| 236 |
+
model = joblib.load(MODEL_FILE)
|
| 237 |
+
scaler = joblib.load(SCALER_FILE)
|
| 238 |
+
last_entry = df.sort_values('date', ascending=False).iloc[0]
|
| 239 |
+
last_week = df.sort_values('date', ascending=False).head(7)
|
| 240 |
+
avg_calories_consumed = last_week['calories_consumed'].mean() + calorie_adjustment
|
| 241 |
+
avg_calories_burned = last_week['calories_burned'].mean() + calorie_adjustment
|
| 242 |
+
avg_exercise = last_week['exercise_minutes'].mean() + exercise_adjustment
|
| 243 |
+
predict_data = pd.DataFrame({
|
| 244 |
+
'date': [last_entry['date'] + datetime.timedelta(days=i) for i in range(1, days+1)],
|
| 245 |
+
'calories_consumed': [avg_calories_consumed] * days,
|
| 246 |
+
'calories_burned': [avg_calories_burned] * days,
|
| 247 |
+
'water_intake': [last_week['water_intake'].mean()] * days,
|
| 248 |
+
'sleep_hours': [last_week['sleep_hours'].mean()] * days,
|
| 249 |
+
'stress_level': [last_week['stress_level'].mean()] * days,
|
| 250 |
+
'exercise_minutes': [avg_exercise] * days
|
| 251 |
+
})
|
| 252 |
+
X_pred = predict_data[['calories_consumed', 'calories_burned', 'water_intake', 'sleep_hours', 'stress_level', 'exercise_minutes']]
|
| 253 |
+
X_pred_scaled = scaler.transform(X_pred)
|
| 254 |
+
predict_data['weight'] = model.predict(X_pred_scaled)
|
| 255 |
+
|
| 256 |
plt.figure(figsize=(10, 6))
|
| 257 |
+
plt.plot(df['date'], df['weight'], 'o-', color='blue', label='Historical')
|
| 258 |
+
plt.plot(predict_data['date'], predict_data['weight'], 'o--', color='red', label='Predicted')
|
| 259 |
+
if goal['target_weight']:
|
| 260 |
+
plt.axhline(y=goal['target_weight'], color='green', linestyle='--', label='Goal')
|
| 261 |
+
plt.title('Weight Trajectory Prediction')
|
| 262 |
plt.xlabel('Date')
|
| 263 |
+
plt.ylabel('Weight')
|
| 264 |
plt.grid(True, alpha=0.3)
|
| 265 |
plt.xticks(rotation=45)
|
|
|
|
| 266 |
plt.legend()
|
| 267 |
+
|
| 268 |
+
start_weight = df['weight'].iloc[-1]
|
| 269 |
+
end_weight = predict_data['weight'].iloc[-1]
|
| 270 |
+
report = f"Starting weight: {start_weight:.1f}\nPredicted weight after {days} days: {end_weight:.1f}\n" + \
|
| 271 |
+
f"Change: {end_weight - start_weight:.1f}"
|
| 272 |
+
recommendations = "Keep tracking consistently!" if end_weight < start_weight else "Consider reducing calories."
|
| 273 |
+
return report, plt, recommendations
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 274 |
|
| 275 |
def train_prediction_model():
|
| 276 |
+
if len(df) < 7:
|
| 277 |
+
return "❌ Not enough data."
|
| 278 |
+
model_df = df.sort_values('date')
|
| 279 |
+
features = ['calories_consumed', 'calories_burned', 'water_intake', 'sleep_hours', 'stress_level', 'exercise_minutes']
|
| 280 |
+
X = model_df[features].fillna(model_df[features].median())
|
| 281 |
+
y = model_df['weight']
|
| 282 |
+
scaler = StandardScaler()
|
| 283 |
+
X_scaled = scaler.fit_transform(X)
|
| 284 |
+
model = LinearRegression()
|
| 285 |
+
model.fit(X_scaled, y)
|
| 286 |
+
joblib.dump(model, MODEL_FILE)
|
| 287 |
+
joblib.dump(scaler, SCALER_FILE)
|
| 288 |
+
return "✅ Model trained!"
|
| 289 |
+
|
| 290 |
+
# Food Log
|
| 291 |
+
def log_food(food_name, quantity):
|
| 292 |
+
if food_name not in food_df['name'].values:
|
| 293 |
+
return "❌ Food not found."
|
| 294 |
+
calories = food_df[food_df['name'] == food_name]['calories'].iloc[0] * quantity
|
| 295 |
+
return f"✅ Logged {quantity} of {food_name} ({calories} calories). Add to your entry!"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 296 |
|
| 297 |
# Gradio Interface
|
| 298 |
+
with gr.Blocks(title="FitJourney", theme=gr.themes.Soft(primary_hue="blue")) as iface:
|
| 299 |
+
gr.Markdown("# 🏋️♀️ FitJourney - Your Weight Loss Companion")
|
| 300 |
+
|
| 301 |
+
with gr.Tab("Dashboard"):
|
| 302 |
+
dashboard_button = gr.Button("Show Dashboard", variant="primary")
|
| 303 |
+
dashboard_text = gr.Markdown()
|
| 304 |
+
dashboard_plot = gr.Plot()
|
| 305 |
+
dashboard_button.click(create_dashboard, outputs=[dashboard_text, dashboard_plot])
|
| 306 |
|
| 307 |
with gr.Tab("Add Entry"):
|
| 308 |
with gr.Row():
|
| 309 |
with gr.Column():
|
| 310 |
date_input = gr.Textbox(label="Date (YYYY-MM-DD)")
|
| 311 |
+
weight_input = gr.Number(label="Weight (lbs)")
|
| 312 |
calories_consumed_input = gr.Number(label="Calories Consumed")
|
| 313 |
calories_burned_input = gr.Number(label="Calories Burned")
|
| 314 |
water_intake_input = gr.Number(label="Water Intake (cups)")
|
| 315 |
sleep_hours_input = gr.Number(label="Sleep Hours")
|
| 316 |
+
stress_level_input = gr.Slider(1, 10, step=1, label="Stress Level")
|
| 317 |
+
mood_input = gr.Dropdown(['Great', 'Good', 'Neutral', 'Poor'], label="Mood")
|
| 318 |
exercise_minutes_input = gr.Number(label="Exercise Minutes")
|
| 319 |
notes_input = gr.Textbox(label="Notes")
|
| 320 |
add_button = gr.Button("Add Entry", variant="primary")
|
| 321 |
add_output = gr.Markdown()
|
| 322 |
+
add_button.click(add_entry, inputs=[date_input, weight_input, calories_consumed_input, calories_burned_input,
|
| 323 |
+
water_intake_input, sleep_hours_input, stress_level_input, mood_input,
|
| 324 |
+
exercise_minutes_input, notes_input], outputs=add_output)
|
|
|
|
|
|
|
| 325 |
|
| 326 |
with gr.Tab("View Entries"):
|
| 327 |
+
num_entries_view = gr.Slider(5, 30, step=5, value=10, label="Entries to View")
|
| 328 |
+
view_button = gr.Button("View", variant="secondary")
|
| 329 |
view_output = gr.DataFrame()
|
| 330 |
view_button.click(view_entries, inputs=num_entries_view, outputs=view_output)
|
| 331 |
|
| 332 |
with gr.Tab("Update Entry"):
|
| 333 |
+
date_update_input = gr.Textbox(label="Date (YYYY-MM-DD)")
|
| 334 |
with gr.Row():
|
| 335 |
with gr.Column():
|
| 336 |
+
weight_update_input = gr.Number(label="Weight (lbs)")
|
| 337 |
calories_consumed_update_input = gr.Number(label="Calories Consumed")
|
| 338 |
calories_burned_update_input = gr.Number(label="Calories Burned")
|
| 339 |
water_intake_update_input = gr.Number(label="Water Intake (cups)")
|
| 340 |
sleep_hours_update_input = gr.Number(label="Sleep Hours")
|
| 341 |
+
stress_level_update_input = gr.Slider(1, 10, step=1, label="Stress Level")
|
| 342 |
+
mood_update_input = gr.Dropdown(['Great', 'Good', 'Neutral', 'Poor'], label="Mood")
|
| 343 |
exercise_minutes_update_input = gr.Number(label="Exercise Minutes")
|
| 344 |
notes_update_input = gr.Textbox(label="Notes")
|
| 345 |
+
update_button = gr.Button("Update", variant="primary")
|
| 346 |
update_output = gr.Markdown()
|
| 347 |
+
update_button.click(update_entry, inputs=[date_update_input, weight_update_input, calories_consumed_update_input,
|
| 348 |
+
calories_burned_update_input, water_intake_update_input, sleep_hours_update_input,
|
| 349 |
+
stress_level_update_input, mood_update_input, exercise_minutes_update_input,
|
| 350 |
+
notes_update_input], outputs=update_output)
|
| 351 |
+
|
| 352 |
+
with gr.Tab("Goals"):
|
| 353 |
+
target_weight_input = gr.Number(label="Target Weight (lbs)")
|
| 354 |
+
target_date_input = gr.Textbox(label="Target Date (YYYY-MM-DD)")
|
| 355 |
+
goal_button = gr.Button("Set Goal", variant="primary")
|
| 356 |
+
goal_output = gr.Markdown()
|
| 357 |
+
goal_view_button = gr.Button("View Goal Progress", variant="secondary")
|
| 358 |
+
goal_view_output = gr.Markdown()
|
| 359 |
+
goal_button.click(set_goal, inputs=[target_weight_input, target_date_input], outputs=goal_output)
|
| 360 |
+
goal_view_button.click(view_goal, outputs=goal_view_output)
|
| 361 |
+
|
| 362 |
+
with gr.Tab("Achievements"):
|
| 363 |
+
achievements_output = gr.Markdown(value=update_achievements())
|
| 364 |
+
gr.Button("Refresh Achievements").click(update_achievements, outputs=achievements_output)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 365 |
|
| 366 |
with gr.Tab("Analytics"):
|
| 367 |
+
weight_plot_button = gr.Button("Weight Over Time", variant="secondary")
|
| 368 |
+
weight_plot_output_text = gr.Markdown()
|
| 369 |
+
weight_plot_output = gr.Image()
|
| 370 |
+
weight_plot_button.click(create_weight_plot, outputs=[weight_plot_output_text, weight_plot_output])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 371 |
|
| 372 |
with gr.Tab("Prediction"):
|
| 373 |
+
days_input = gr.Slider(7, 90, step=7, value=30, label="Days to Predict")
|
| 374 |
+
calorie_adjustment_input = gr.Number(label="Calorie Adjustment")
|
| 375 |
+
exercise_adjustment_input = gr.Number(label="Exercise Adjustment")
|
| 376 |
+
predict_button = gr.Button("Predict", variant="primary")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 377 |
prediction_output_text = gr.Markdown()
|
| 378 |
prediction_plot_output = gr.Plot()
|
| 379 |
recommendation_output = gr.Markdown()
|
| 380 |
+
predict_button.click(predict_weight_trajectory, inputs=[days_input, calorie_adjustment_input, exercise_adjustment_input],
|
|
|
|
|
|
|
| 381 |
outputs=[prediction_output_text, prediction_plot_output, recommendation_output])
|
| 382 |
|
| 383 |
with gr.Tab("Train Model"):
|
| 384 |
+
train_button = gr.Button("Train Model", variant="primary")
|
| 385 |
train_output = gr.Markdown()
|
| 386 |
+
train_button.click(train_prediction_model, outputs=train_output)
|
| 387 |
|
| 388 |
+
with gr.Tab("Food Log"):
|
| 389 |
+
food_name_input = gr.Dropdown(choices=food_df['name'].tolist(), label="Food Item")
|
| 390 |
+
quantity_input = gr.Number(label="Quantity", minimum=1, value=1)
|
| 391 |
+
food_log_button = gr.Button("Log Food", variant="secondary")
|
| 392 |
+
food_log_output = gr.Markdown()
|
| 393 |
+
food_log_button.click(log_food, inputs=[food_name_input, quantity_input], outputs=food_log_output)
|
| 394 |
|
| 395 |
+
with gr.Tab("Sample Data"):
|
| 396 |
def load_sample_data():
|
|
|
|
| 397 |
global df
|
| 398 |
+
start_date = datetime.datetime.now() - datetime.timedelta(days=30)
|
| 399 |
+
sample_data = [{'date': start_date + datetime.timedelta(days=i),
|
| 400 |
+
'weight': 180 - (0.1 * i) + np.random.normal(0, 0.3),
|
| 401 |
+
'calories_consumed': np.random.randint(1800, 2300),
|
| 402 |
+
'calories_burned': np.random.randint(2000, 2500),
|
| 403 |
+
'water_intake': np.random.randint(5, 10),
|
| 404 |
+
'sleep_hours': round(np.random.normal(7, 0.5), 1),
|
| 405 |
+
'stress_level': np.random.randint(3, 8),
|
| 406 |
+
'mood': np.random.choice(['Great', 'Good', 'Neutral', 'Poor']),
|
| 407 |
+
'exercise_minutes': np.random.randint(20, 60),
|
| 408 |
+
'notes': ''} for i in range(30)]
|
| 409 |
+
df = pd.concat([df, pd.DataFrame(sample_data)], ignore_index=True)
|
| 410 |
+
df['date'] = pd.to_datetime(df['date'])
|
| 411 |
+
df = df.sort_values('date', ascending=False)
|
| 412 |
+
df.to_csv(DATA_FILE, index=False)
|
| 413 |
+
update_achievements()
|
| 414 |
+
return "✅ Sample data loaded!"
|
| 415 |
+
sample_button = gr.Button("Load Sample Data", variant="secondary")
|
| 416 |
+
sample_output = gr.Markdown()
|
| 417 |
+
sample_button.click(load_sample_data, outputs=sample_output)
|
| 418 |
|
|
|
|
| 419 |
iface.launch(debug=False)
|