Spaces:
Sleeping
Sleeping
| from fastapi import APIRouter, Depends, HTTPException, UploadFile, File, status | |
| from sqlalchemy.orm import Session | |
| from typing import List | |
| import io | |
| import re | |
| import os | |
| import json | |
| from app.database import get_db | |
| from app import models, schemas | |
| from app.auth import get_current_user | |
| try: | |
| import google.generativeai as genai | |
| GEMINI_AVAILABLE = True | |
| except ImportError: | |
| GEMINI_AVAILABLE = False | |
| genai = None | |
| router = APIRouter(prefix="/nutrition", tags=["nutrition"]) | |
| GEMINI_API_KEY = "AIzaSyD2H-Oct8iMxsvpaDC_oaqgB1FiTzRF62k" | |
| if GEMINI_API_KEY and GEMINI_AVAILABLE and genai: | |
| genai.configure(api_key="AIzaSyD2H-Oct8iMxsvpaDC_oaqgB1FiTzRF62k") | |
| def analyze_with_gemini(user_health_issues: List[models.HealthIssue], all_products: List[models.Product], image_data: bytes) -> dict: | |
| if not GEMINI_API_KEY: | |
| return { | |
| "extracted_nutrition": {}, | |
| "health_rating": 5.0, | |
| "health_recommendations": ["Gemini API key not configured. Please set GEMINI_API_KEY environment variable."], | |
| "suggested_alternatives": [], | |
| "analysis_summary": "Analysis unavailable without Gemini API key." | |
| } | |
| try: | |
| model = genai.GenerativeModel('gemini-2.0-flash') | |
| health_issues_text = ", ".join([str(issue.issue_type) for issue in user_health_issues]) if user_health_issues else "None reported" | |
| products_text = "" | |
| if all_products: | |
| products_text = "\n".join([ | |
| f"- {str(p.name)} (Brand: {str(p.brand) if p.brand else 'N/A'}): Calories: {p.calories}, Protein: {p.protein}g, Fat: {p.fat}g, Carbs: {p.carbohydrates}g, Sodium: {p.sodium}mg, Sugar: {p.sugar}g" | |
| for p in all_products[:10] | |
| ]) | |
| prompt_text = f"""Analyze this nutrition label image. | |
| First, extract the following nutrition facts from the image: | |
| - Calories | |
| - Protein (g) | |
| - Fat (g) | |
| - Sugar (g) | |
| - Fiber (g) | |
| - Extract others if they're available | |
| Then, based on the extracted data and the user's health issues, provide health recommendations. | |
| User's Health Issues: {health_issues_text} | |
| Available Alternative Products: | |
| {products_text if products_text else "No products in database"} | |
| Please provide: | |
| 1. The extracted nutrition data in JSON format. | |
| 2. A health rating from 1-10 (where 1 is unhealthy and 10 is very healthy) | |
| 3. 3-5 specific health recommendations based on the user's health issues | |
| 4. Names of 2-3 healthier alternative products from the list above (if available) | |
| 5. A brief analysis summary | |
| Format your response strictly as follows: | |
| ```json | |
| {{ | |
| "nutrition": {{ | |
| "calories": 0.0, | |
| "protein": 0.0, | |
| "fat": 0.0, | |
| "carbohydrates": 0.0, | |
| "sugar": 0.0, | |
| "fiber": 0.0, | |
| }}, | |
| "rating": 0.0, | |
| "recommendations": [ | |
| "recommendation 1", | |
| "recommendation 2" | |
| ], | |
| "alternatives": [ | |
| "product name 1", | |
| "product name 2" | |
| ], | |
| "summary": "your analysis summary" | |
| }} | |
| ``` | |
| """ | |
| content = [prompt_text] | |
| if image_data: | |
| image_part = { | |
| "mime_type": "image/jpeg", | |
| "data": image_data | |
| } | |
| content.append(image_part) | |
| response = model.generate_content(content) | |
| result_text = response.text | |
| # Clean up the response to get just the JSON part | |
| json_match = re.search(r'```json\s*(.*?)\s*```', result_text, re.DOTALL) | |
| if json_match: | |
| json_str = json_match.group(1) | |
| else: | |
| # Try to find the first { and last } | |
| start = result_text.find('{') | |
| end = result_text.rfind('}') | |
| if start != -1 and end != -1: | |
| json_str = result_text[start:end+1] | |
| else: | |
| raise ValueError("Could not parse JSON from Gemini response") | |
| result_data = json.loads(json_str) | |
| extracted_nutrition = result_data.get("nutrition", {}) | |
| health_rating = float(result_data.get("rating", 5.0)) | |
| recommendations = result_data.get("recommendations", []) | |
| alternative_names = result_data.get("alternatives", []) | |
| analysis_summary = result_data.get("summary", "Analysis completed.") | |
| suggested_products = [] | |
| for alt_name in alternative_names: | |
| for product in all_products: | |
| if alt_name.lower() in product.name.lower(): | |
| suggested_products.append(product) | |
| break | |
| return { | |
| "extracted_nutrition": extracted_nutrition, | |
| "health_rating": health_rating, | |
| "health_recommendations": recommendations, | |
| "suggested_alternatives": suggested_products[:3], | |
| "analysis_summary": analysis_summary | |
| } | |
| except Exception as e: | |
| print(f"Gemini Error: {e}") | |
| return { | |
| "extracted_nutrition": {}, | |
| "health_rating": 5.0, | |
| "health_recommendations": [f"Error during analysis: {str(e)}"], | |
| "suggested_alternatives": [], | |
| "analysis_summary": "Analysis failed due to an error." | |
| } | |
| async def analyze_nutrition_image( | |
| file: UploadFile = File(...), | |
| current_user: models.User = Depends(get_current_user), | |
| db: Session = Depends(get_db) | |
| ): | |
| if not file.content_type or not file.content_type.startswith('image/'): | |
| raise HTTPException(status_code=400, detail="File must be an image") | |
| image_data = await file.read() | |
| user_health_issues = db.query(models.HealthIssue).filter( | |
| models.HealthIssue.user_id == current_user.id | |
| ).all() | |
| all_products = db.query(models.Product).all() | |
| gemini_analysis = analyze_with_gemini(user_health_issues, all_products, image_data) | |
| return schemas.NutritionAnalysisResponse( | |
| extracted_nutrition=gemini_analysis["extracted_nutrition"], | |
| health_rating=gemini_analysis["health_rating"], | |
| health_recommendations=gemini_analysis["health_recommendations"], | |
| suggested_alternatives=gemini_analysis["suggested_alternatives"], | |
| analysis_summary=gemini_analysis["analysis_summary"] | |
| ) | |