import json import random import math from typing import Dict, List, Any, Optional from dataclasses import dataclass from datetime import datetime, timedelta # USDA provider removed - using local food database only @dataclass class UserProfile: """User profile information""" age: int sex: str # "male" or "female" height_cm: float weight_kg: float activity_level: str # "sedentary", "light", "moderate", "active", "very_active" goal: str # "weight_loss", "muscle_gain", "maintenance", "keto", "mediterranean" dietary_restrictions: List[str] = None allergies: List[str] = None preferences: List[str] = None @dataclass class MealPlan: """Meal plan""" meal_name: str time_range: str foods: List[Dict[str, Any]] total_calories: float total_protein: float total_carbs: float total_fat: float total_fiber: float @dataclass class DailyPlan: """Daily diet plan""" date: str meals: List[MealPlan] total_calories: float total_protein: float total_carbs: float total_fat: float total_fiber: float class DietPlanner: """Diet planner class""" def __init__(self, diet_data_file: str = "foods_DB_Example.json"): """Load diet data""" with open(diet_data_file, 'r', encoding='utf-8') as f: self.diet_data = json.load(f) # Meal name translation mapping self.meal_name_translation = { "Kahvaltı": "Breakfast", "Kuşluk": "Morning Snack", "Öğle Yemeği": "Lunch", "İkindi": "Afternoon Snack", "Akşam Yemeği": "Dinner", "Gece Atıştırması": "Evening Snack" } def calculate_bmr(self, profile: UserProfile) -> float: """Calculate basal metabolic rate (Mifflin-St Jeor formula)""" if profile.sex.lower() == "male": bmr = 10 * profile.weight_kg + 6.25 * profile.height_cm - 5 * profile.age + 5 else: bmr = 10 * profile.weight_kg + 6.25 * profile.height_cm - 5 * profile.age - 161 return bmr def calculate_tdee(self, profile: UserProfile) -> float: """Calculate total daily energy expenditure""" bmr = self.calculate_bmr(profile) activity_multipliers = { "sedentary": 1.2, # Sedentary "light": 1.375, # Lightly active "moderate": 1.55, # Moderately active "active": 1.725, # Active "very_active": 1.9 # Very active } multiplier = activity_multipliers.get(profile.activity_level.lower(), 1.2) return bmr * multiplier def calculate_target_calories(self, profile: UserProfile) -> float: """Calculate target calorie amount""" tdee = self.calculate_tdee(profile) if profile.goal == "weight_loss": # 15-20% calorie deficit deficit = random.uniform(0.15, 0.20) return tdee * (1 - deficit) elif profile.goal == "muscle_gain": # 10-15% calorie surplus surplus = random.uniform(0.10, 0.15) return tdee * (1 + surplus) elif profile.goal == "keto": # 10-15% calorie deficit deficit = random.uniform(0.10, 0.15) return tdee * (1 - deficit) else: # Maintenance and Mediterranean return tdee def get_macro_ratios(self, profile: UserProfile) -> Dict[str, float]: """Get macronutrient ratios""" diet_type = self.diet_data["diet_types"].get(profile.goal, {}) if profile.goal == "weight_loss": return { "protein": random.uniform(0.25, 0.30), "carbs": random.uniform(0.40, 0.45), "fat": random.uniform(0.25, 0.30) } elif profile.goal == "muscle_gain": return { "protein": random.uniform(0.25, 0.30), "carbs": random.uniform(0.45, 0.55), "fat": random.uniform(0.20, 0.25) } elif profile.goal == "keto": return { "protein": random.uniform(0.20, 0.25), "carbs": random.uniform(0.05, 0.10), "fat": random.uniform(0.70, 0.75) } else: return { "protein": random.uniform(0.20, 0.25), "carbs": random.uniform(0.45, 0.55), "fat": random.uniform(0.25, 0.30) } def get_available_foods(self, profile: UserProfile) -> Dict[str, List[Dict]]: """Filter foods that the user can eat""" available_foods = {} for category, subcategories in self.diet_data["foods"].items(): available_foods[category] = [] for subcategory, foods in subcategories.items(): for food in foods: # Allergy check if profile.allergies: food_name_lower = food["name"].lower() if any(allergy.lower() in food_name_lower for allergy in profile.allergies): continue # Dietary restrictions check if profile.dietary_restrictions: if "vegetarian" in profile.dietary_restrictions and category == "protein_sources": if subcategory in ["lean_meats", "fish_seafood"]: continue if "vegan" in profile.dietary_restrictions and category in ["protein_sources", "dairy"]: if subcategory in ["lean_meats", "fish_seafood", "eggs", "milk_products", "cheese"]: continue # Calorie value check (prevent division by zero) if food.get("calories_per_100g", 0) <= 0: continue available_foods[category].append(food) return available_foods def select_foods_for_meal(self, meal_type: str, target_calories: float, macro_ratios: Dict[str, float], available_foods: Dict[str, List[Dict]]) -> List[Dict[str, Any]]: """Select foods for meal""" meal_info = self.diet_data["meals"][meal_type] food_categories = meal_info["food_categories"] selected_foods = [] remaining_calories = target_calories # Select food for each category for category in food_categories: if category not in available_foods or not available_foods[category]: continue # Calorie target for category category_calories = remaining_calories * 0.3 # 30% for each category if category_calories < 50: # If very few calories left continue # Randomly select food from category food = random.choice(available_foods[category]) # Calculate portion amount (with division by zero check) if food["calories_per_100g"] <= 0: continue # Skip foods with 0 or negative calorie values portion_grams = min(category_calories / (food["calories_per_100g"] / 100), 200) # Minimum portion check if portion_grams < 20: continue selected_food = { "name": food["name"], "portion_grams": round(portion_grams, 1), "calories": round(food["calories_per_100g"] * portion_grams / 100, 1), "protein": round(food["protein_per_100g"] * portion_grams / 100, 1), "carbs": round(food["carbs_per_100g"] * portion_grams / 100, 1), "fat": round(food["fat_per_100g"] * portion_grams / 100, 1), "fiber": round(food["fiber_per_100g"] * portion_grams / 100, 1), "vitamins": food["vitamins"], "minerals": food["minerals"] } selected_foods.append(selected_food) remaining_calories -= selected_food["calories"] if remaining_calories < 100: # Stop if very few calories left break return selected_foods def create_meal_plan(self, meal_type: str, target_calories: float, macro_ratios: Dict[str, float], available_foods: Dict[str, List[Dict]]) -> MealPlan: """Create meal plan""" meal_info = self.diet_data["meals"][meal_type] foods = self.select_foods_for_meal(meal_type, target_calories, macro_ratios, available_foods) # Calculate total nutritional values total_calories = sum(food["calories"] for food in foods) total_protein = sum(food["protein"] for food in foods) total_carbs = sum(food["carbs"] for food in foods) total_fat = sum(food["fat"] for food in foods) total_fiber = sum(food["fiber"] for food in foods) # Translate meal name to English meal_name = self.meal_name_translation.get(meal_info["name"], meal_info["name"]) return MealPlan( meal_name=meal_name, time_range=meal_info["time_range"], foods=foods, total_calories=round(total_calories, 1), total_protein=round(total_protein, 1), total_carbs=round(total_carbs, 1), total_fat=round(total_fat, 1), total_fiber=round(total_fiber, 1) ) def generate_daily_plan(self, profile: UserProfile) -> DailyPlan: """Generate daily diet plan""" target_calories = self.calculate_target_calories(profile) macro_ratios = self.get_macro_ratios(profile) available_foods = self.get_available_foods(profile) # Determine meal types meal_types = ["breakfast", "lunch", "dinner"] # Add snacks if profile.goal == "muscle_gain": meal_types.extend(["morning_snack", "afternoon_snack", "evening_snack"]) elif profile.goal == "weight_loss": meal_types.append("morning_snack") else: meal_types.extend(["morning_snack", "afternoon_snack"]) meals = [] total_calories = 0 total_protein = 0 total_carbs = 0 total_fat = 0 total_fiber = 0 for meal_type in meal_types: # Calorie target for meal meal_calories = target_calories * float(self.diet_data["meals"][meal_type]["calorie_target"].split("-")[0]) / 100 meal_plan = self.create_meal_plan(meal_type, meal_calories, macro_ratios, available_foods) meals.append(meal_plan) total_calories += meal_plan.total_calories total_protein += meal_plan.total_protein total_carbs += meal_plan.total_carbs total_fat += meal_plan.total_fat total_fiber += meal_plan.total_fiber return DailyPlan( date=datetime.now().strftime("%Y-%m-%d"), meals=meals, total_calories=round(total_calories, 1), total_protein=round(total_protein, 1), total_carbs=round(total_carbs, 1), total_fat=round(total_fat, 1), total_fiber=round(total_fiber, 1) ) def generate_weekly_plan(self, profile: UserProfile) -> List[DailyPlan]: """Generate weekly diet plan""" weekly_plan = [] for i in range(7): # Create different variations for each day daily_plan = self.generate_daily_plan(profile) daily_plan.date = (datetime.now() + timedelta(days=i)).strftime("%Y-%m-%d") weekly_plan.append(daily_plan) return weekly_plan def plan_to_dict(self, plan: DailyPlan) -> Dict[str, Any]: """Convert plan to dictionary format""" return { "date": plan.date, "meals": [ { "meal_name": meal.meal_name, "time_range": meal.time_range, "foods": meal.foods, "nutrition": { "calories": meal.total_calories, "protein": meal.total_protein, "carbs": meal.total_carbs, "fat": meal.total_fat, "fiber": meal.total_fiber } } for meal in plan.meals ], "daily_totals": { "calories": plan.total_calories, "protein": plan.total_protein, "carbs": plan.total_carbs, "fat": plan.total_fat, "fiber": plan.total_fiber } } def generate_diet_plan(user_profile: Dict[str, Any], plan_type: str = "daily") -> Dict[str, Any]: """ Create diet plan Args: user_profile: User profile information plan_type: "daily" or "weekly" Returns: Diet plan dictionary """ try: # Create UserProfile object profile = UserProfile( age=user_profile["age"], sex=user_profile["sex"], height_cm=user_profile["height_cm"], weight_kg=user_profile["weight_kg"], activity_level=user_profile["activity_level"], goal=user_profile["goal"], dietary_restrictions=user_profile.get("dietary_restrictions", []), allergies=user_profile.get("allergies", []), preferences=user_profile.get("preferences", []) ) # Create DietPlanner planner = DietPlanner() if plan_type == "weekly": plans = planner.generate_weekly_plan(profile) return { "plan_type": "weekly", "plans": [planner.plan_to_dict(plan) for plan in plans] } else: plan = planner.generate_daily_plan(profile) return { "plan_type": "daily", "plan": planner.plan_to_dict(plan) } except Exception as e: return { "error": f"Error while creating diet plan: {str(e)}" } def calculate_nutrition_info(food_name: str, portion_grams: float) -> Dict[str, Any]: """ Calculate nutritional values for a specific food Args: food_name: Food name portion_grams: Portion amount (grams) Returns: Nutritional values dictionary """ try: planner = DietPlanner() # Find the food for category, subcategories in planner.diet_data["foods"].items(): for subcategory, foods in subcategories.items(): for food in foods: if food["name"].lower() == food_name.lower(): return { "food_name": food["name"], "portion_grams": portion_grams, "calories": round(food["calories_per_100g"] * portion_grams / 100, 1), "protein": round(food["protein_per_100g"] * portion_grams / 100, 1), "carbs": round(food["carbs_per_100g"] * portion_grams / 100, 1), "fat": round(food["fat_per_100g"] * portion_grams / 100, 1), "fiber": round(food["fiber_per_100g"] * portion_grams / 100, 1), "vitamins": food["vitamins"], "minerals": food["minerals"] } # If not found locally, return error (USDA fetching removed) return {"error": f"'{food_name}' not found in local database"} except Exception as e: return {"error": f"Error while calculating nutritional value: {str(e)}"} def get_diet_recommendations(user_profile: Dict[str, Any]) -> Dict[str, Any]: """ Create diet recommendations for user Args: user_profile: User profile information Returns: Diet recommendations dictionary """ try: profile = UserProfile( age=user_profile["age"], sex=user_profile["sex"], height_cm=user_profile["height_cm"], weight_kg=user_profile["weight_kg"], activity_level=user_profile["activity_level"], goal=user_profile["goal"], dietary_restrictions=user_profile.get("dietary_restrictions", []), allergies=user_profile.get("allergies", []), preferences=user_profile.get("preferences", []) ) planner = DietPlanner() bmr = planner.calculate_bmr(profile) tdee = planner.calculate_tdee(profile) target_calories = planner.calculate_target_calories(profile) macro_ratios = planner.get_macro_ratios(profile) return { "bmr": round(bmr, 1), "tdee": round(tdee, 1), "target_calories": round(target_calories, 1), "macro_ratios": { "protein": f"{macro_ratios['protein']*100:.1f}%", "carbs": f"{macro_ratios['carbs']*100:.1f}%", "fat": f"{macro_ratios['fat']*100:.1f}%" }, "macro_grams": { "protein": round(target_calories * macro_ratios["protein"] / 4, 1), "carbs": round(target_calories * macro_ratios["carbs"] / 4, 1), "fat": round(target_calories * macro_ratios["fat"] / 9, 1) }, "recommendations": { "meal_frequency": planner.diet_data["diet_types"][profile.goal]["meal_frequency"], "fiber_target": planner.diet_data["diet_types"][profile.goal]["fiber_target"] } } except Exception as e: return {"error": f"Error while creating recommendations: {str(e)}"} # Test function if __name__ == "__main__": # Example user profile test_profile = { "age": 30, "sex": "male", "height_cm": 175, "weight_kg": 75, "activity_level": "moderate", "goal": "weight_loss", "dietary_restrictions": [], "allergies": [], "preferences": [] } # Create daily plan daily_plan = generate_diet_plan(test_profile, "daily") print("Daily Diet Plan:") print(json.dumps(daily_plan, ensure_ascii=False, indent=2)) # Get recommendations recommendations = get_diet_recommendations(test_profile) print("\nDiet Recommendations:") print(json.dumps(recommendations, ensure_ascii=False, indent=2))