from flask import Blueprint, Response, request, jsonify, current_app from app.models.recipe import Recipe import json import asyncio import os import sys import traceback import resource from app.services import extraction from app.services import image_query api_bp = Blueprint('api', __name__, url_prefix='/api') def log_memory(tag=""): """Helper to print current RAM usage to logs""" try: usage = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss # Linux returns KB, convert to MB usage_mb = usage / 1024 print(f"[{tag}] MEMORY USAGE: {usage_mb:.2f} MB", file=sys.stderr) except Exception: pass @api_bp.route('/form-data', methods=['GET']) def get_form_data(): file_path = os.path.join(os.getcwd(), 'form_data.json') try: with open(file_path, 'r') as file: data = file.read() return data except FileNotFoundError: print(f"Error: File not found at {file_path}", file=sys.stderr) return jsonify({"error": f"File not found at {file_path}"}), 404 except Exception as e: traceback.print_exc(file=sys.stderr) return jsonify({"error": str(e)}), 500 @api_bp.route('/recommend', methods=['POST']) async def recommend_recipes(): print(">>> RECOMMEND ENDPOINT HIT", file=sys.stderr) log_memory("Start Recommend") try: data = request.json category = data.get('category') dietary_preference = data.get('dietary_preference') ingredients = data.get('ingredients', []) calories = data.get('calories') time = data.get('time') keywords = data.get('keywords', []) keywords_name = data.get('keywords_name', []) if calories is not None: calories = int(calories) if time is not None: time = int(time) feature_weights_recommend = { 'ingredients': 0.15, 'category': 0.25, 'dietary': 0.20, 'calories': 0.10, 'time': 0.10, 'keywords': 0.10, 'keywords_name': 0.10 } recommendations = await current_app.recommendation_system.get_recommendations( category=category, dietary_preference=dietary_preference, ingredients=ingredients, calories=calories, time=time, keywords=keywords, keywords_name=keywords_name, feature_weights=feature_weights_recommend ) log_memory("End Recommend") return jsonify([vars(recipe) for recipe in recommendations]) except Exception as e: print("!!! CRITICAL ERROR IN RECOMMEND !!!", file=sys.stderr) traceback.print_exc(file=sys.stderr) return jsonify({"error": str(e)}), 500 @api_bp.route('/extract-recipe-attributes', methods=['POST']) async def recommend_recipes2(): print(">>> SEARCH (EXTRACT) ENDPOINT HIT", file=sys.stderr) log_memory("Start Search") try: data = request.get_json() if not data: return jsonify({"error": "No data provided"}), 400 raw_text = data.get('text') if not raw_text: return jsonify({"error": "No search text provided"}), 400 # 1. Extract Attributes (Using Gemini/AI) print(f"Extracting attributes for: {raw_text[:50]}...", file=sys.stderr) extracted_info = extraction.extract_recipe_attributes(raw_text) if 'error' in extracted_info: print(f"Extraction Error: {extracted_info}", file=sys.stderr) return jsonify(extracted_info), 500 feature_weights_extract = { 'ingredients': 0.50, 'category': 0.0, 'dietary': 0.0, 'calories': 0.0, 'time': 0.0, 'keywords': 0.40, 'keywords_name': 0.10 } category = extracted_info.get('category', '') calories = extracted_info.get('calories', None) time = extracted_info.get('time', None) keywords = extracted_info.get('keywords', []) keywords_name = extracted_info.get('keywords_name', []) ingredients = extracted_info.get('ingredients', []) try: calories = int(calories) if calories else None time = int(time) if time else None except (ValueError, TypeError): return jsonify({"error": "Invalid calories or time value"}), 400 # 2. Get Recommendations (Using Huge DataFrame) print("Calling Recommendation System...", file=sys.stderr) recommendations = await current_app.recommendation_system.get_recommendations( category=category, ingredients=ingredients, calories=calories, time=time, keywords=keywords, keywords_name=keywords_name, feature_weights=feature_weights_extract ) log_memory("End Search") recipe_list = [vars(recipe) for recipe in recommendations] return jsonify(recipe_list) except Exception as e: print("!!! CRITICAL ERROR IN SEARCH !!!", file=sys.stderr) # This is the line that will finally show you WHY it crashes traceback.print_exc(file=sys.stderr) return jsonify({"error": str(e)}), 500 @api_bp.route('/analyze-food-image', methods=['POST']) async def handle_analyze_food_image(): print(">>> IMAGE ANALYSIS ENDPOINT HIT", file=sys.stderr) log_memory("Start Image") try: if 'image' not in request.files: return jsonify({"error": "No image file provided"}), 400 file = request. files['image'] if file.filename == '': return jsonify({"error": "No selected file"}), 400 print("Analyzing Food Image...", file=sys.stderr) description = image_query.analyze_food_image(file) print(f"Image Description: {description[:50]}...", file=sys.stderr) # 🔥 ADD MORE LOGGING HERE print(f"Full Image Description: {description}", file=sys.stderr) extracted_info = extraction.extract_recipe_attributes(description) # 🔥 LOG THE EXTRACTION RESULT print(f"Extraction Result: {extracted_info}", file=sys.stderr) if 'error' in extracted_info: # 🔥 DON'T STOP - CONTINUE WITH EMPTY VALUES print(f"Extraction warning (continuing anyway): {extracted_info}", file=sys.stderr) extracted_info = { "category": "", "calories": None, "time": None, "keywords": description.split(', ')[: 5], # Use first 5 words from description "keywords_name": description.split(', ')[:3], "ingredients": description.split(', ') } feature_weights_extract = { 'ingredients': 0.50, 'category': 0.0, 'dietary': 0.0, 'calories': 0.0, 'time': 0.0, 'keywords': 0.40, 'keywords_name': 0.10 } category = extracted_info.get('category', '') calories = extracted_info.get('calories', None) time = extracted_info. get('time', None) keywords = extracted_info.get('keywords', []) keywords_name = extracted_info.get('keywords_name', []) ingredients = extracted_info.get('ingredients', []) try: calories = int(calories) if calories else None time = int(time) if time else None except (ValueError, TypeError): calories = None time = None print("Calling Recommendation System...", file=sys.stderr) recommendations = await current_app.recommendation_system.get_recommendations( category=category, ingredients=ingredients, calories=calories, time=time, keywords=keywords, keywords_name=keywords_name, feature_weights=feature_weights_extract ) log_memory("End Image") recipe_list = [vars(recipe) for recipe in recommendations] return jsonify(recipe_list) except Exception as e: print("! !! CRITICAL ERROR IN IMAGE ANALYSIS !! !", file=sys.stderr) traceback.print_exc(file=sys.stderr) return jsonify({"error": str(e)}), 500 @api_bp.route('/health', methods=['GET']) def health_check(): return jsonify({ "status": "alive", "message": "Recipe Recommendation Service is Running", "endpoints": [ "/api/form-data", "/api/recommend", "/api/extract-recipe-attributes", "/api/analyze-food-image" ] }), 200