from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import status, permissions from .rag_service import generate_chat_response from finance.utils_mongo import get_user_db_id import tempfile import os import pdfplumber class ChatView(APIView): permission_classes = [permissions.IsAuthenticated] def post(self, request): query = request.data.get('message', '') provider = request.data.get('provider') is_voice = request.data.get('is_voice', False) attached_file = request.FILES.get('file') # 1. Process PDF Attachment (Text Extraction) if attached_file and attached_file.name.lower().endswith('.pdf'): try: with pdfplumber.open(attached_file) as pdf: pdf_text = "" for page in pdf.pages: pdf_text += (page.extract_text() or "") + "\n" if pdf_text: query = f"[ATTACHED PDF CONTENT: {attached_file.name}]\n{pdf_text}\n\nUSER QUESTION: {query}" except Exception as e: print(f"PDF extraction error: {e}") # 2. Process Image Attachment (Temporary Save for Vision) image_path = None if attached_file and any(attached_file.name.lower().endswith(ext) for ext in ['.png', '.jpg', '.jpeg', '.webp']): try: suffix = os.path.splitext(attached_file.name)[1] with tempfile.NamedTemporaryFile(delete=False, suffix=suffix) as tmp: for chunk in attached_file.chunks(): tmp.write(chunk) image_path = tmp.name except Exception as e: print(f"Image save error: {e}") if not query and not image_path: return Response({"error": "Message or attachment is required"}, status=status.HTTP_400_BAD_REQUEST) mongo_user_id = get_user_db_id(request.user) auth_header = request.headers.get('Authorization', '') access_token = auth_header.replace('Bearer ', '').strip() if auth_header.startswith('Bearer ') else None if is_voice: from finance.ai_helper import generate_voice_response result = generate_voice_response(query, provider=provider) else: result = generate_chat_response( mongo_user_id, query, provider=provider, access_token=access_token, image_path=image_path ) # Cleanup temporary image file if image_path and os.path.exists(image_path): try: os.remove(image_path) except: pass # Save to MongoDB (Embedded in User) try: from expense_tracker.utils import MongoDBClient from datetime import datetime from bson import ObjectId db = MongoDBClient.get_client() chat_log = { '_id': ObjectId(), 'query': query, 'response': result.get('response'), 'context_used': result.get('context_used'), 'timestamp': datetime.now() } db.users.update_one( {'_id': mongo_user_id}, {'$push': {'chat_history': chat_log}} ) except Exception as e: pass # Silently fail if chat log saving fails return Response(result) def get(self, request): """Retrieve chat history for the user""" try: from expense_tracker.utils import MongoDBClient from bson import ObjectId db = MongoDBClient.get_client() db = MongoDBClient.get_client() mongo_user_id = get_user_db_id(request.user) user = db.users.find_one({'_id': mongo_user_id}, {'chat_history': 1}) history = [] if user and 'chat_history' in user: # Return last 50 entries raw_history = user['chat_history'][-50:] for h in raw_history: # Format for frontend with ID for deletion history.append({ 'id': str(h.get('_id', '')), 'sender': 'user', 'text': h.get('query'), 'timestamp': h.get('timestamp') }) history.append({ 'id': str(h.get('_id', '')), 'sender': 'bot', 'text': h.get('response'), 'timestamp': h.get('timestamp') }) return Response(history) except Exception as e: return Response({"error": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) def delete(self, request, msg_id=None): """Delete a specific chat entry from history""" if not msg_id: return Response({"error": "Message ID required"}, status=status.HTTP_400_BAD_REQUEST) try: from expense_tracker.utils import MongoDBClient from bson import ObjectId db = MongoDBClient.get_client() db = MongoDBClient.get_client() mongo_user_id = get_user_db_id(request.user) if msg_id == 'all': result = db.users.update_one( {'_id': mongo_user_id}, {'$set': {'chat_history': []}} ) if result.modified_count > 0: return Response({"message": "All chat history cleared"}, status=status.HTTP_200_OK) return Response({"message": "No chat history to clear"}, status=status.HTTP_200_OK) result = db.users.update_one( {'_id': mongo_user_id}, {'$pull': {'chat_history': {'_id': ObjectId(msg_id)}}} ) if result.modified_count > 0: return Response({"message": "Chat deleted successfully"}, status=status.HTTP_200_OK) else: return Response({"error": "Chat not found"}, status=status.HTTP_404_NOT_FOUND) except Exception as e: return Response({"error": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)