NutritionAIAPI / app /routes /nutrition.py
Sakshi
nutrition
96f792c
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."
}
@router.post("/analyze", response_model=schemas.NutritionAnalysisResponse)
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"]
)