Spaces:
Sleeping
Sleeping
| 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 | |
| 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 | |
| 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 | |
| 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)) |