Spaces:
Runtime error
Runtime error
| from flask import Blueprint, request, jsonify | |
| import requests | |
| import os | |
| from dotenv import load_dotenv | |
| from utils.jwt_helper import decode_jwt | |
| from models.history import History | |
| load_dotenv() | |
| recommend_bp = Blueprint('recommend', __name__) | |
| RECOMMENDER_ENDPOINTS = { | |
| "movie": os.getenv("MOVIE_RECOMMENDER_URL"), | |
| "book": os.getenv("BOOK_RECOMMENDER_URL"), | |
| "tv": os.getenv("TV_RECOMMENDER_URL") | |
| } | |
| RESPONSE_KEYS = { | |
| "movie": "movies", | |
| "book": "books", | |
| "tv": "shows" | |
| } | |
| def recommend_tv(): | |
| try: | |
| data = request.get_json() | |
| if not data: | |
| return jsonify({"error": "No data provided"}), 400 | |
| rec_type = data.get('type') | |
| genre = data.get('genre') | |
| top_k = data.get('top_k', 10) | |
| if not rec_type or not genre: | |
| return jsonify({"error": "Missing 'type' or 'genre'"}), 400 | |
| if rec_type not in RECOMMENDER_ENDPOINTS or not RECOMMENDER_ENDPOINTS[rec_type]: | |
| return jsonify({"error": "Invalid or missing recommender URL for type."}), 400 | |
| # JWT Authentication | |
| auth_header = request.headers.get('Authorization') | |
| if not auth_header or not auth_header.startswith('Bearer '): | |
| return jsonify({"error": "Missing or invalid Authorization header"}), 401 | |
| token = auth_header.split(" ")[1] | |
| try: | |
| user_data = decode_jwt(token) | |
| user_id = user_data.get("user_id") | |
| except Exception as e: | |
| return jsonify({"error": "Invalid or expired token"}), 401 | |
| # Process genres | |
| genres_list = [g.strip() for g in genre.split(",")] if isinstance(genre, str) else genre | |
| # Call microservice | |
| try: | |
| recommender_url = RECOMMENDER_ENDPOINTS[rec_type] | |
| response = requests.post( | |
| recommender_url, | |
| json={"genre": genre}, # Changed from "genres": genres_list to "genre": genre | |
| timeout=10 | |
| ) | |
| if not response.ok: | |
| return jsonify({ | |
| "error": f"{rec_type} service error", | |
| "details": response.text, | |
| "status_code": response.status_code | |
| }), 500 | |
| result = response.json() | |
| # Handle TV microservice's specific response format | |
| if isinstance(result, list): | |
| # Direct list response | |
| raw_items = result | |
| elif "recommendations" in result: | |
| # TV microservice format: {"genre": "Tragedy", "recommendations": [...]} | |
| recommendations = result.get("recommendations", []) | |
| # Convert string recommendations to objects | |
| raw_items = [] | |
| for rec in recommendations: | |
| if isinstance(rec, str): | |
| # Convert string to object format | |
| raw_items.append({ | |
| "name": rec, | |
| "title": rec, | |
| "description": "", | |
| "genre": [result.get("genre", "")], | |
| "rating": None | |
| }) | |
| else: | |
| # Already an object | |
| raw_items.append(rec) | |
| else: | |
| # Standard format with status | |
| if result.get("status") != "success": | |
| error_msg = result.get("message") or result.get("error") or "Unknown error" | |
| return jsonify({"error": error_msg}), 500 | |
| raw_items = result.get(RESPONSE_KEYS.get(rec_type, "items"), []) | |
| # Normalize response format | |
| normalized = [] | |
| for item in raw_items: | |
| normalized.append({ | |
| "type": rec_type, | |
| "name": item.get("name") or item.get("title"), | |
| "creator": item.get("director") or item.get("author") or item.get("creator"), | |
| "description": item.get("description", ""), | |
| "genre": item.get("genre", []), | |
| "rating": item.get("rating"), | |
| "year": item.get("year"), | |
| "image_url": item.get("image_url") | |
| }) | |
| # Save to history | |
| try: | |
| history = History( | |
| user_id=user_id, | |
| recommendation_type=rec_type, | |
| genre=genres_list, | |
| items=normalized, | |
| query_params={"top_k": top_k} | |
| ) | |
| history.save() | |
| print(f"Saved recommendation history for user {user_id}") | |
| except Exception as e: | |
| print(f"Failed to save history: {e}") | |
| # Don't fail the request if history saving fails | |
| return jsonify({ | |
| "status": "success", | |
| "recommendations": normalized, | |
| "count": len(normalized), | |
| "type": rec_type, | |
| "genres": genres_list | |
| }), 200 | |
| except requests.exceptions.Timeout: | |
| return jsonify({"error": f"{rec_type} service timeout"}), 504 | |
| except requests.exceptions.RequestException as e: | |
| return jsonify({"error": f"Failed to connect to {rec_type} service", "details": str(e)}), 503 | |
| except Exception as e: | |
| print(f"Recommend error: {e}") | |
| return jsonify({"error": "Internal server error"}), 500 | |
| def recommend_movie(): | |
| try: | |
| data = request.get_json() | |
| if not data: | |
| return jsonify({"error": "No data provided"}), 400 | |
| rec_type = data.get('type') | |
| genre = data.get('genre') | |
| top_k = data.get('top_k', 10) | |
| if not rec_type or not genre: | |
| return jsonify({"error": "Missing 'type' or 'genre'"}), 400 | |
| if rec_type not in RECOMMENDER_ENDPOINTS or not RECOMMENDER_ENDPOINTS[rec_type]: | |
| return jsonify({"error": "Invalid or missing recommender URL for type."}), 400 | |
| # JWT Authentication | |
| auth_header = request.headers.get('Authorization') | |
| if not auth_header or not auth_header.startswith('Bearer '): | |
| return jsonify({"error": "Missing or invalid Authorization header"}), 401 | |
| token = auth_header.split(" ")[1] | |
| try: | |
| user_data = decode_jwt(token) | |
| user_id = user_data.get("user_id") | |
| except Exception as e: | |
| return jsonify({"error": "Invalid or expired token"}), 401 | |
| # Process genres | |
| genres_list = [g.strip() for g in genre.split(",")] if isinstance(genre, str) else genre | |
| # Call microservice | |
| try: | |
| recommender_url = RECOMMENDER_ENDPOINTS[rec_type] | |
| response = requests.post( | |
| recommender_url, | |
| json={"genres": genres_list, "top_k": top_k}, | |
| timeout=10 | |
| ) | |
| if not response.ok: | |
| return jsonify({ | |
| "error": f"{rec_type} service error", | |
| "details": response.text, | |
| "status_code": response.status_code | |
| }), 500 | |
| result = response.json() | |
| if result.get("status") != "success": | |
| return jsonify({"error": result.get("message", "Unknown error")}), 500 | |
| raw_items = result.get(RESPONSE_KEYS.get(rec_type, "items"), []) | |
| # Normalize response format | |
| normalized = [] | |
| for item in raw_items: | |
| normalized.append({ | |
| "type": rec_type, | |
| "name": item.get("name") or item.get("title"), | |
| "creator": item.get("director") or item.get("author") or item.get("creator"), | |
| "description": item.get("description", ""), | |
| "genre": item.get("genre", []), | |
| "rating": item.get("rating") | |
| }) | |
| # Save to history | |
| try: | |
| history = History( | |
| user_id=user_id, | |
| recommendation_type=rec_type, | |
| genre=genres_list, | |
| items=normalized, | |
| query_params={"top_k": top_k} | |
| ) | |
| history.save() | |
| print(f"Saved recommendation history for user {user_id}") | |
| except Exception as e: | |
| print(f"Failed to save history: {e}") | |
| # Don't fail the request if history saving fails | |
| return jsonify({ | |
| "status": "success", | |
| "recommendations": normalized, | |
| "count": len(normalized), | |
| "type": rec_type, | |
| "genres": genres_list | |
| }), 200 | |
| except requests.exceptions.Timeout: | |
| return jsonify({"error": f"{rec_type} service timeout"}), 504 | |
| except requests.exceptions.RequestException as e: | |
| return jsonify({"error": f"Failed to connect to {rec_type} service", "details": str(e)}), 503 | |
| except Exception as e: | |
| print(f"Recommend error: {e}") | |
| return jsonify({"error": "Internal server error"}), 500 | |
| def recommend_book(): | |
| try: | |
| data = request.get_json() | |
| if not data: | |
| return jsonify({"error": "No data provided"}), 400 | |
| rec_type = data.get('type') | |
| genre = data.get('genre') | |
| top_k = data.get('top_k', 10) | |
| if not rec_type or not genre: | |
| return jsonify({"error": "Missing 'type' or 'genre'"}), 400 | |
| if rec_type not in RECOMMENDER_ENDPOINTS or not RECOMMENDER_ENDPOINTS[rec_type]: | |
| return jsonify({"error": "Invalid or missing recommender URL for type."}), 400 | |
| # JWT Authentication | |
| auth_header = request.headers.get('Authorization') | |
| if not auth_header or not auth_header.startswith('Bearer '): | |
| return jsonify({"error": "Missing or invalid Authorization header"}), 401 | |
| token = auth_header.split(" ")[1] | |
| try: | |
| user_data = decode_jwt(token) | |
| user_id = user_data.get("user_id") | |
| except Exception as e: | |
| return jsonify({"error": "Invalid or expired token"}), 401 | |
| # Call microservice | |
| try: | |
| recommender_url = RECOMMENDER_ENDPOINTS[rec_type] | |
| response = requests.post( | |
| recommender_url, | |
| json={"genre": genre}, # Single string as expected | |
| timeout=10 | |
| ) | |
| if not response.ok: | |
| return jsonify({ | |
| "error": f"{rec_type} service error", | |
| "details": response.text, | |
| "status_code": response.status_code | |
| }), 500 | |
| result = response.json() | |
| # Handle different response formats | |
| if isinstance(result, list): | |
| # Microservice returns a direct list of books | |
| raw_items = result | |
| else: | |
| # Microservice returns structured response with status | |
| if result.get("status") != "success": | |
| return jsonify({"error": result.get("message", "Unknown error")}), 500 | |
| raw_items = result.get(RESPONSE_KEYS.get(rec_type, "items"), []) | |
| # Normalize response format | |
| normalized = [] | |
| for item in raw_items: | |
| normalized.append({ | |
| "type": rec_type, | |
| "name": item.get("name") or item.get("Title"), | |
| "creator": item.get("director") or item.get("Author") or item.get("creator"), | |
| # "description": item.get("description", ""), | |
| "genre": item.get("genre", []), | |
| "rating": item.get("avg_rating") | |
| }) | |
| # Save to history | |
| try: | |
| history = History( | |
| user_id=user_id, | |
| recommendation_type=rec_type, | |
| genre=[genre], # Fixed: wrap single genre in list for History model | |
| items=normalized, | |
| query_params={"top_k": top_k} | |
| ) | |
| history.save() | |
| print(f"Saved recommendation history for user {user_id}") | |
| except Exception as e: | |
| print(f"Failed to save history: {e}") | |
| # Don't fail the request if history saving fails | |
| return jsonify({ | |
| "status": "success", | |
| "recommendations": normalized, | |
| "count": len(normalized), | |
| "type": rec_type, | |
| "genres": [genre] #Fixed: wrap single genre in list for consistency | |
| }), 200 | |
| except requests.exceptions.Timeout: | |
| return jsonify({"error": f"{rec_type} service timeout"}), 504 | |
| except requests.exceptions.RequestException as e: | |
| return jsonify({"error": f"Failed to connect to {rec_type} service", "details": str(e)}), 503 | |
| except Exception as e: | |
| print(f"Recommend error: {e}") | |
| return jsonify({"error": "Internal server error"}), 500 |