"""API client for RewardPilot Orchestrator""" import httpx from typing import Dict, Optional from config import ORCHESTRATOR_URL from typing import Any, Dict, List, Optional, Tuple import logging # Create logger logger = logging.getLogger(__name__) class RewardPilotClient: """Client for interacting with RewardPilot Orchestrator""" def __init__(self, base_url: str = ORCHESTRATOR_URL): self.base_url = base_url.rstrip('/') self.timeout = 30.0 async def get_recommendation( self, user_id: str, merchant: str, mcc: str, amount_usd: float, transaction_date: Optional[str] = None ) -> Dict: """Get card recommendation from orchestrator""" async with httpx.AsyncClient(timeout=self.timeout) as client: try: payload = { "user_id": user_id, "merchant": merchant, "mcc": mcc, "amount_usd": amount_usd } if transaction_date: payload["transaction_date"] = transaction_date response = await client.post( f"{self.base_url}/recommend", json=payload ) response.raise_for_status() return response.json() except httpx.HTTPError as e: return { "error": True, "message": f"API Error: {str(e)}" } except Exception as e: return { "error": True, "message": f"Unexpected error: {str(e)}" } def get_recommendation_sync( self, user_id: str, merchant: str, mcc: str, amount_usd: float, transaction_date: Optional[str] = None ) -> Dict: """Synchronous version for Gradio""" import asyncio try: loop = asyncio.get_event_loop() except RuntimeError: loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) return loop.run_until_complete( self.get_recommendation( user_id, merchant, mcc, amount_usd, transaction_date ) ) def get_user_analytics(self, user_id: str) -> Dict[str, Any]: """ Fetch user analytics including spending patterns and optimization metrics Args: user_id: User identifier Returns: Dictionary containing analytics data """ try: # Option 1: If you have a dedicated analytics endpoint response = httpx.get( f"{self.orchestrator_url}/analytics/{user_id}", timeout=30.0 ) response.raise_for_status() return response.json() except Exception as e: logger.error(f"Analytics fetch failed: {e}") # Return mock data as fallback return self._get_mock_analytics(user_id) def _get_mock_analytics(self, user_id: str) -> Dict[str, Any]: """Generate mock analytics data for demo purposes""" import random # Different data per user for variety user_multipliers = { "u_alice": 1.2, "u_bob": 0.9, "u_charlie": 1.5, } multiplier = user_multipliers.get(user_id, 1.0) return { "user_id": user_id, "annual_savings": round(342 * multiplier, 2), "rate_increase": round(23 * multiplier, 1), "optimized_transactions": int(156 * multiplier), "optimization_score": min(100, int(87 * multiplier)), "category_breakdown": [ { "category": "🛒 Groceries", "monthly_spend": round(450 * multiplier, 2), "best_card": "Amex Gold", "rewards": round(27 * multiplier, 2), "rate": "6%" }, { "category": "🍽️ Restaurants", "monthly_spend": round(320 * multiplier, 2), "best_card": "Amex Gold", "rewards": round(12.8 * multiplier, 2), "rate": "4%" }, { "category": "⛽ Gas", "monthly_spend": round(180 * multiplier, 2), "best_card": "Costco Visa", "rewards": round(7.2 * multiplier, 2), "rate": "4%" }, { "category": "✈️ Travel", "monthly_spend": round(850 * multiplier, 2), "best_card": "Sapphire Reserve", "rewards": round(42.5 * multiplier, 2), "rate": "5%" }, { "category": "🎬 Entertainment", "monthly_spend": round(125 * multiplier, 2), "best_card": "Freedom Unlimited", "rewards": round(1.88 * multiplier, 2), "rate": "1.5%" }, { "category": "🏪 Online Shopping", "monthly_spend": round(280 * multiplier, 2), "best_card": "Amazon Prime", "rewards": round(16.8 * multiplier, 2), "rate": "6%" } ], "total_monthly_spend": round(2205 * multiplier, 2), "total_monthly_rewards": round(108.18 * multiplier, 2), "average_rate": round(4.9, 1), "top_categories": [ {"name": "✈️ Travel", "amount": round(850 * multiplier, 2), "change": "+45%"}, {"name": "🛒 Groceries", "amount": round(450 * multiplier, 2), "change": "+12%"}, {"name": "🍽️ Restaurants", "amount": round(320 * multiplier, 2), "change": "-5%"} ], "ytd_rewards": round(1298.16 * multiplier, 2), "ytd_potential": round(1640 * multiplier, 2), "money_left": round(341.84 * multiplier, 2), "forecast": { "next_month_spend": round(2350 * multiplier, 2), "next_month_rewards": round(115.25 * multiplier, 2), "cards_to_watch": ["Amex Gold (dining cap)", "Freedom (quarterly bonus)"] }, "recommendations": [ "💳 Use Chase Freedom for groceries in Q4 (5% back)", "⚠️ Monitor Amex Gold dining spend (cap at $2,000)", "🎯 Book holiday travel with Sapphire Reserve for 5x points" ] }