sammy786 commited on
Commit
94ff613
·
verified ·
1 Parent(s): 7b8affe

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +121 -44
app.py CHANGED
@@ -16,41 +16,88 @@ def safe_get(data: Dict, key: str, default: Any = None) -> Any:
16
 
17
 
18
  def normalize_recommendation_data(data: Dict) -> Dict:
19
- """Normalize API response to ensure all required fields exist"""
 
 
 
20
 
21
- # Extract or compute rewards_earned
22
- rewards_earned = safe_get(data, 'rewards_earned')
23
- if rewards_earned is None:
24
- amount = safe_get(data, 'amount', 0)
25
- rewards_rate = safe_get(data, 'rewards_rate', '0x points')
 
 
 
26
 
27
- try:
28
- if 'x' in str(rewards_rate):
29
- multiplier = float(rewards_rate.split('x')[0])
30
- rewards_earned = amount * (multiplier / 100)
31
- elif '%' in str(rewards_rate):
32
- multiplier = float(rewards_rate.replace('%', '').split()[0])
33
- rewards_earned = amount * (multiplier / 100)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
  else:
35
- rewards_earned = 0
36
- except:
37
- rewards_earned = 0
 
 
 
 
38
 
39
- return {
40
- 'recommended_card': safe_get(data, 'recommended_card', 'Unknown Card'),
41
- 'rewards_earned': round(float(rewards_earned), 2),
42
- 'rewards_rate': safe_get(data, 'rewards_rate', 'N/A'),
43
- 'merchant': safe_get(data, 'merchant', 'Unknown Merchant'),
44
- 'category': safe_get(data, 'category', 'Unknown'),
45
- 'amount': float(safe_get(data, 'amount', 0)),
46
- 'annual_potential': float(safe_get(data, 'annual_potential', rewards_earned * 12)),
 
 
 
 
 
 
 
 
 
47
  'optimization_score': int(safe_get(data, 'optimization_score', 75)),
48
- 'reasoning': safe_get(data, 'reasoning', 'Optimal choice for this category'),
49
- 'warnings': safe_get(data, 'warnings', []),
50
- 'alternatives': safe_get(data, 'alternatives', []),
51
  'mock_data': safe_get(data, 'mock_data', False)
52
  }
53
 
 
 
54
  from datetime import date
55
  from typing import Optional, Tuple, List, Dict, Any
56
  import gradio as gr
@@ -158,20 +205,24 @@ def get_recommendation_with_ai(user_id, merchant, category, amount):
158
  return "❌ Please enter a valid amount greater than $0.", None
159
 
160
  try:
 
 
 
161
  # Get base recommendation from orchestrator
162
- result = client.get_recommendation(
163
  user_id=user_id,
164
  merchant=merchant,
165
- category=category,
166
- amount=float(amount)
167
  )
168
 
169
- if not result.get('success'):
 
170
  error_msg = result.get('error', 'Unknown error')
171
  return f"❌ Error: {error_msg}", None
172
 
173
  # Normalize the data to ensure all fields exist
174
- data = normalize_recommendation_data(result.get('data', {}))
175
 
176
  # Generate LLM explanation if enabled
177
  ai_explanation = ""
@@ -226,6 +277,7 @@ def get_recommendation_with_ai(user_id, merchant, category, amount):
226
 
227
  - **Category:** {data['category']}
228
  - **Merchant:** {data['merchant']}
 
229
  - **Annual Potential:** ${data['annual_potential']:.2f}
230
  - **Optimization Score:** {data['optimization_score']}/100
231
  """
@@ -239,11 +291,8 @@ def get_recommendation_with_ai(user_id, merchant, category, amount):
239
  # Add alternatives
240
  if data['alternatives']:
241
  output += "\n\n### 🔄 Alternative Options\n\n"
242
- for alt in data['alternatives'][:3]:
243
- alt_rewards = safe_get(alt, 'rewards', 0)
244
- alt_rate = safe_get(alt, 'rate', 'N/A')
245
- alt_card = safe_get(alt, 'card', 'Unknown')
246
- output += f"- **{alt_card}:** ${alt_rewards:.2f} ({alt_rate})\n"
247
 
248
  # Create visualization
249
  chart = create_rewards_comparison_chart(data)
@@ -256,18 +305,36 @@ def get_recommendation_with_ai(user_id, merchant, category, amount):
256
  print(f"Recommendation error: {error_details}")
257
  return f"❌ Error: {str(e)}\n\nPlease check your API connection or try again.", None
258
 
 
259
  def create_rewards_comparison_chart(data: Dict) -> go.Figure:
260
- """Create rewards comparison chart"""
261
 
262
  try:
263
  # Prepare data for chart
264
  cards = [data['recommended_card']]
265
  rewards = [data['rewards_earned']]
 
266
 
267
  # Add alternatives
268
  for alt in data.get('alternatives', [])[:3]:
269
- cards.append(safe_get(alt, 'card', 'Unknown'))
270
- rewards.append(float(safe_get(alt, 'rewards', 0)))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
271
 
272
  # Create bar chart
273
  fig = go.Figure(data=[
@@ -275,36 +342,46 @@ def create_rewards_comparison_chart(data: Dict) -> go.Figure:
275
  x=cards,
276
  y=rewards,
277
  marker=dict(
278
- color=['#667eea'] + ['#a0aec0'] * (len(cards) - 1),
279
  line=dict(color='white', width=2)
280
  ),
281
  text=[f'${r:.2f}' for r in rewards],
282
  textposition='outside',
 
283
  )
284
  ])
285
 
286
  fig.update_layout(
287
- title='Rewards Comparison',
 
 
 
 
288
  xaxis_title='Credit Card',
289
  yaxis_title='Rewards Earned ($)',
290
  template='plotly_white',
291
  height=400,
292
  showlegend=False,
293
- margin=dict(t=50, b=50, l=50, r=50)
 
294
  )
295
 
296
  return fig
297
 
298
  except Exception as e:
299
  print(f"Chart creation error: {e}")
 
 
 
300
  # Return empty chart with error message
301
  fig = go.Figure()
302
  fig.add_annotation(
303
- text=f"Error creating chart: {str(e)}",
304
  xref="paper", yref="paper",
305
  x=0.5, y=0.5, showarrow=False,
306
  font=dict(size=14, color="red")
307
  )
 
308
  return fig
309
 
310
  def get_analytics_with_insights(user_id):
 
16
 
17
 
18
  def normalize_recommendation_data(data: Dict) -> Dict:
19
+ """
20
+ Normalize API response to ensure all required fields exist.
21
+ Handles both orchestrator format and direct API format.
22
+ """
23
 
24
+ # Handle case where recommended_card is a dict (orchestrator format)
25
+ recommended_card = safe_get(data, 'recommended_card', {})
26
+ if isinstance(recommended_card, dict):
27
+ card_name = safe_get(recommended_card, 'card_name', 'Unknown Card')
28
+ reward_amount = float(safe_get(recommended_card, 'reward_amount', 0))
29
+ reward_rate = float(safe_get(recommended_card, 'reward_rate', 0))
30
+ category = safe_get(recommended_card, 'category', 'Unknown')
31
+ reasoning = safe_get(recommended_card, 'reasoning', 'Optimal choice')
32
 
33
+ # Format reward rate as string
34
+ if reward_rate > 0:
35
+ rewards_rate_str = f"{reward_rate}x points"
36
+ else:
37
+ rewards_rate_str = "N/A"
38
+ else:
39
+ # Handle case where it's already a string
40
+ card_name = str(recommended_card) if recommended_card else 'Unknown Card'
41
+ reward_amount = float(safe_get(data, 'rewards_earned', 0))
42
+ reward_rate = 0
43
+ rewards_rate_str = safe_get(data, 'rewards_rate', 'N/A')
44
+ category = safe_get(data, 'category', 'Unknown')
45
+ reasoning = safe_get(data, 'reasoning', 'Optimal choice')
46
+
47
+ # Get merchant and amount from top level
48
+ merchant = safe_get(data, 'merchant', 'Unknown Merchant')
49
+ amount = float(safe_get(data, 'amount_usd', safe_get(data, 'amount', 0)))
50
+
51
+ # Calculate annual potential
52
+ annual_potential = reward_amount * 12 if reward_amount > 0 else 0
53
+
54
+ # Process alternative cards
55
+ alternatives = []
56
+ alt_cards = safe_get(data, 'alternative_cards', safe_get(data, 'alternatives', []))
57
+
58
+ for alt in alt_cards[:3]: # Limit to top 3
59
+ if isinstance(alt, dict):
60
+ alt_name = safe_get(alt, 'card_name', safe_get(alt, 'card', 'Unknown'))
61
+ alt_reward = float(safe_get(alt, 'reward_amount', safe_get(alt, 'rewards', 0)))
62
+ alt_rate = safe_get(alt, 'reward_rate', safe_get(alt, 'rate', 0))
63
+
64
+ if isinstance(alt_rate, (int, float)) and alt_rate > 0:
65
+ alt_rate_str = f"{alt_rate}x points"
66
  else:
67
+ alt_rate_str = str(alt_rate) if alt_rate else "N/A"
68
+
69
+ alternatives.append({
70
+ 'card': alt_name,
71
+ 'rewards': alt_reward,
72
+ 'rate': alt_rate_str
73
+ })
74
 
75
+ # Extract warnings
76
+ warnings = safe_get(data, 'warnings', [])
77
+ forecast_warning = safe_get(data, 'forecast_warning')
78
+ if forecast_warning and isinstance(forecast_warning, dict):
79
+ warning_msg = safe_get(forecast_warning, 'warning_message')
80
+ if warning_msg:
81
+ warnings.append(warning_msg)
82
+
83
+ # Build normalized response
84
+ normalized = {
85
+ 'recommended_card': card_name,
86
+ 'rewards_earned': round(reward_amount, 2),
87
+ 'rewards_rate': rewards_rate_str,
88
+ 'merchant': merchant,
89
+ 'category': category,
90
+ 'amount': amount,
91
+ 'annual_potential': round(annual_potential, 2),
92
  'optimization_score': int(safe_get(data, 'optimization_score', 75)),
93
+ 'reasoning': reasoning,
94
+ 'warnings': warnings,
95
+ 'alternatives': alternatives,
96
  'mock_data': safe_get(data, 'mock_data', False)
97
  }
98
 
99
+ return normalized
100
+
101
  from datetime import date
102
  from typing import Optional, Tuple, List, Dict, Any
103
  import gradio as gr
 
205
  return "❌ Please enter a valid amount greater than $0.", None
206
 
207
  try:
208
+ # Map category to MCC
209
+ mcc = MCC_CATEGORIES.get(category, "5999")
210
+
211
  # Get base recommendation from orchestrator
212
+ result = client.get_recommendation_sync(
213
  user_id=user_id,
214
  merchant=merchant,
215
+ mcc=mcc,
216
+ amount_usd=float(amount)
217
  )
218
 
219
+ # Check for errors
220
+ if result.get('error'):
221
  error_msg = result.get('error', 'Unknown error')
222
  return f"❌ Error: {error_msg}", None
223
 
224
  # Normalize the data to ensure all fields exist
225
+ data = normalize_recommendation_data(result)
226
 
227
  # Generate LLM explanation if enabled
228
  ai_explanation = ""
 
277
 
278
  - **Category:** {data['category']}
279
  - **Merchant:** {data['merchant']}
280
+ - **Reasoning:** {data['reasoning']}
281
  - **Annual Potential:** ${data['annual_potential']:.2f}
282
  - **Optimization Score:** {data['optimization_score']}/100
283
  """
 
291
  # Add alternatives
292
  if data['alternatives']:
293
  output += "\n\n### 🔄 Alternative Options\n\n"
294
+ for alt in data['alternatives']:
295
+ output += f"- **{alt['card']}:** ${alt['rewards']:.2f} ({alt['rate']})\n"
 
 
 
296
 
297
  # Create visualization
298
  chart = create_rewards_comparison_chart(data)
 
305
  print(f"Recommendation error: {error_details}")
306
  return f"❌ Error: {str(e)}\n\nPlease check your API connection or try again.", None
307
 
308
+
309
  def create_rewards_comparison_chart(data: Dict) -> go.Figure:
310
+ """Create rewards comparison chart with proper error handling"""
311
 
312
  try:
313
  # Prepare data for chart
314
  cards = [data['recommended_card']]
315
  rewards = [data['rewards_earned']]
316
+ colors = ['#667eea'] # Primary color for recommended card
317
 
318
  # Add alternatives
319
  for alt in data.get('alternatives', [])[:3]:
320
+ cards.append(alt['card'])
321
+ rewards.append(float(alt['rewards']))
322
+ colors.append('#a0aec0') # Gray for alternatives
323
+
324
+ # Only create chart if we have valid data
325
+ if not cards or all(r == 0 for r in rewards):
326
+ fig = go.Figure()
327
+ fig.add_annotation(
328
+ text="No rewards data available for comparison",
329
+ xref="paper", yref="paper",
330
+ x=0.5, y=0.5, showarrow=False,
331
+ font=dict(size=14, color="#666")
332
+ )
333
+ fig.update_layout(
334
+ height=400,
335
+ template='plotly_white'
336
+ )
337
+ return fig
338
 
339
  # Create bar chart
340
  fig = go.Figure(data=[
 
342
  x=cards,
343
  y=rewards,
344
  marker=dict(
345
+ color=colors,
346
  line=dict(color='white', width=2)
347
  ),
348
  text=[f'${r:.2f}' for r in rewards],
349
  textposition='outside',
350
+ hovertemplate='<b>%{x}</b><br>Rewards: $%{y:.2f}<extra></extra>'
351
  )
352
  ])
353
 
354
  fig.update_layout(
355
+ title={
356
+ 'text': 'Rewards Comparison',
357
+ 'x': 0.5,
358
+ 'xanchor': 'center'
359
+ },
360
  xaxis_title='Credit Card',
361
  yaxis_title='Rewards Earned ($)',
362
  template='plotly_white',
363
  height=400,
364
  showlegend=False,
365
+ margin=dict(t=60, b=50, l=50, r=50),
366
+ hovermode='x'
367
  )
368
 
369
  return fig
370
 
371
  except Exception as e:
372
  print(f"Chart creation error: {e}")
373
+ import traceback
374
+ print(traceback.format_exc())
375
+
376
  # Return empty chart with error message
377
  fig = go.Figure()
378
  fig.add_annotation(
379
+ text=f"Error creating chart",
380
  xref="paper", yref="paper",
381
  x=0.5, y=0.5, showarrow=False,
382
  font=dict(size=14, color="red")
383
  )
384
+ fig.update_layout(height=400, template='plotly_white')
385
  return fig
386
 
387
  def get_analytics_with_insights(user_id):