first commit
This commit is contained in:
258
calorie_tracker_app/utils.py
Normal file
258
calorie_tracker_app/utils.py
Normal file
@@ -0,0 +1,258 @@
|
||||
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
|
||||
Reference in New Issue
Block a user