259 lines
7.6 KiB
Python
259 lines
7.6 KiB
Python
from datetime import date, timedelta
|
|
from models import db, Meal, WaterLog, WeightLog, DailySummary, UserGoal
|
|
|
|
def calculate_bmr(weight_kg, height_cm, age, gender):
|
|
"""
|
|
Calculate Basal Metabolic Rate using Mifflin-St Jeor Equation
|
|
"""
|
|
if gender.lower() == 'male':
|
|
bmr = (10 * weight_kg) + (6.25 * height_cm) - (5 * age) + 5
|
|
else: # female
|
|
bmr = (10 * weight_kg) + (6.25 * height_cm) - (5 * age) - 161
|
|
|
|
return round(bmr)
|
|
|
|
def calculate_tdee(bmr, activity_level):
|
|
"""
|
|
Calculate Total Daily Energy Expenditure
|
|
"""
|
|
multipliers = {
|
|
'sedentary': 1.2,
|
|
'light': 1.375,
|
|
'moderate': 1.55,
|
|
'active': 1.725,
|
|
'very_active': 1.9
|
|
}
|
|
|
|
multiplier = multipliers.get(activity_level, 1.55)
|
|
return round(bmr * multiplier)
|
|
|
|
def calculate_macro_targets(weight_kg, goal_type='recomp'):
|
|
"""
|
|
Calculate macro targets based on body weight and goal
|
|
"""
|
|
if goal_type == 'muscle_gain':
|
|
protein = weight_kg * 2.4 # High protein for muscle building
|
|
carbs = weight_kg * 3.5 # Higher carbs for energy
|
|
fat = weight_kg * 1.0 # Moderate fat
|
|
elif goal_type == 'weight_loss':
|
|
protein = weight_kg * 2.2 # High protein to preserve muscle
|
|
carbs = weight_kg * 2.0 # Lower carbs for deficit
|
|
fat = weight_kg * 0.8 # Lower fat
|
|
else: # recomp (body recomposition)
|
|
protein = weight_kg * 2.2 # High protein
|
|
carbs = weight_kg * 2.5 # Moderate carbs
|
|
fat = weight_kg * 0.9 # Moderate fat
|
|
|
|
return {
|
|
'protein_g': round(protein),
|
|
'carbs_g': round(carbs),
|
|
'fat_g': round(fat)
|
|
}
|
|
|
|
def calculate_daily_totals(user_id, target_date=None):
|
|
"""
|
|
Calculate total nutrition consumed for a given date
|
|
"""
|
|
if target_date is None:
|
|
target_date = date.today()
|
|
|
|
# Get all meals for the date
|
|
meals = Meal.query.filter_by(user_id=user_id, date=target_date).all()
|
|
|
|
totals = {
|
|
'calories': 0,
|
|
'protein': 0,
|
|
'carbs': 0,
|
|
'fat': 0,
|
|
'meals': []
|
|
}
|
|
|
|
for meal in meals:
|
|
meal_totals = meal.calculate_totals()
|
|
totals['calories'] += meal_totals['calories']
|
|
totals['protein'] += meal_totals['protein']
|
|
totals['carbs'] += meal_totals['carbs']
|
|
totals['fat'] += meal_totals['fat']
|
|
|
|
totals['meals'].append({
|
|
'id': meal.id,
|
|
'type': meal.meal_type,
|
|
'time': meal.time.strftime('%H:%M') if meal.time else None,
|
|
'totals': meal_totals,
|
|
'foods': [
|
|
{
|
|
'name': mf.food.name,
|
|
'quantity': mf.quantity,
|
|
'calories': mf.calories_consumed
|
|
}
|
|
for mf in meal.foods
|
|
]
|
|
})
|
|
|
|
return totals
|
|
|
|
def calculate_water_total(user_id, target_date=None):
|
|
"""
|
|
Calculate total water intake for a given date
|
|
"""
|
|
if target_date is None:
|
|
target_date = date.today()
|
|
|
|
water_logs = WaterLog.query.filter_by(user_id=user_id, date=target_date).all()
|
|
total = sum(log.amount_ml for log in water_logs)
|
|
|
|
return {
|
|
'total_ml': total,
|
|
'logs': [
|
|
{
|
|
'id': log.id,
|
|
'amount_ml': log.amount_ml,
|
|
'time': log.time.strftime('%H:%M') if log.time else None
|
|
}
|
|
for log in water_logs
|
|
]
|
|
}
|
|
|
|
def get_weight_trend(user_id, days=7):
|
|
"""
|
|
Get weight trend for the past N days
|
|
"""
|
|
end_date = date.today()
|
|
start_date = end_date - timedelta(days=days-1)
|
|
|
|
weight_logs = WeightLog.query.filter(
|
|
WeightLog.user_id == user_id,
|
|
WeightLog.date >= start_date,
|
|
WeightLog.date <= end_date
|
|
).order_by(WeightLog.date).all()
|
|
|
|
return [
|
|
{
|
|
'date': log.date.strftime('%Y-%m-%d'),
|
|
'weight_kg': log.weight_kg
|
|
}
|
|
for log in weight_logs
|
|
]
|
|
|
|
def get_calorie_trend(user_id, days=7):
|
|
"""
|
|
Get calorie intake trend for the past N days
|
|
"""
|
|
end_date = date.today()
|
|
start_date = end_date - timedelta(days=days-1)
|
|
|
|
trend = []
|
|
current_date = start_date
|
|
|
|
while current_date <= end_date:
|
|
totals = calculate_daily_totals(user_id, current_date)
|
|
trend.append({
|
|
'date': current_date.strftime('%Y-%m-%d'),
|
|
'calories': round(totals['calories']),
|
|
'protein': round(totals['protein']),
|
|
'carbs': round(totals['carbs']),
|
|
'fat': round(totals['fat'])
|
|
})
|
|
current_date += timedelta(days=1)
|
|
|
|
return trend
|
|
|
|
def update_daily_summary(user_id, target_date=None):
|
|
"""
|
|
Update or create daily summary for a user
|
|
"""
|
|
if target_date is None:
|
|
target_date = date.today()
|
|
|
|
# Calculate totals
|
|
nutrition = calculate_daily_totals(user_id, target_date)
|
|
water = calculate_water_total(user_id, target_date)
|
|
|
|
# Get weight for the day
|
|
weight_log = WeightLog.query.filter_by(user_id=user_id, date=target_date).first()
|
|
weight = weight_log.weight_kg if weight_log else None
|
|
|
|
# Get user's calorie target
|
|
from models import User
|
|
user = User.query.get(user_id)
|
|
target_calories = user.target_daily_calories if user else 2000
|
|
|
|
# Find or create summary
|
|
summary = DailySummary.query.filter_by(user_id=user_id, date=target_date).first()
|
|
|
|
if not summary:
|
|
summary = DailySummary(user_id=user_id, date=target_date)
|
|
db.session.add(summary)
|
|
|
|
# Update values
|
|
summary.total_calories = nutrition['calories']
|
|
summary.total_protein_g = nutrition['protein']
|
|
summary.total_carbs_g = nutrition['carbs']
|
|
summary.total_fat_g = nutrition['fat']
|
|
summary.total_water_ml = water['total_ml']
|
|
summary.calories_remaining = target_calories - nutrition['calories']
|
|
summary.weight_kg = weight
|
|
|
|
try:
|
|
db.session.commit()
|
|
return summary
|
|
except Exception as e:
|
|
db.session.rollback()
|
|
print(f"Error updating daily summary: {e}")
|
|
return None
|
|
|
|
def get_macro_percentages(protein_g, carbs_g, fat_g):
|
|
"""
|
|
Calculate macro distribution as percentages
|
|
"""
|
|
protein_cal = protein_g * 4
|
|
carbs_cal = carbs_g * 4
|
|
fat_cal = fat_g * 9
|
|
total_cal = protein_cal + carbs_cal + fat_cal
|
|
|
|
if total_cal == 0:
|
|
return {'protein': 0, 'carbs': 0, 'fat': 0}
|
|
|
|
return {
|
|
'protein': round((protein_cal / total_cal) * 100),
|
|
'carbs': round((carbs_cal / total_cal) * 100),
|
|
'fat': round((fat_cal / total_cal) * 100)
|
|
}
|
|
|
|
def suggest_foods_for_macros(remaining_protein, remaining_carbs, remaining_fat):
|
|
"""
|
|
Suggest Filipino foods based on remaining macros
|
|
Returns category suggestions
|
|
"""
|
|
suggestions = []
|
|
|
|
# High protein needed
|
|
if remaining_protein > 30:
|
|
suggestions.append({
|
|
'category': 'High Protein Ulam',
|
|
'examples': ['Grilled Tilapia', 'Chicken Tinola', 'Grilled Chicken']
|
|
})
|
|
|
|
# High carbs needed
|
|
if remaining_carbs > 40:
|
|
suggestions.append({
|
|
'category': 'Carbs',
|
|
'examples': ['White Rice', 'Pandesal', 'Sweet Potato']
|
|
})
|
|
|
|
# High fat needed
|
|
if remaining_fat > 20:
|
|
suggestions.append({
|
|
'category': 'Healthy Fats',
|
|
'examples': ['Sisig', 'Lechon Kawali', 'Bicol Express']
|
|
})
|
|
|
|
# Balanced meal needed
|
|
if remaining_protein > 20 and remaining_carbs > 30:
|
|
suggestions.append({
|
|
'category': 'Balanced Meals',
|
|
'examples': ['Tapsilog', 'Chicken Adobo with Rice', 'Sinigang']
|
|
})
|
|
|
|
return suggestions
|