sammy786 commited on
Commit
4d1957f
Β·
verified Β·
1 Parent(s): a450617

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +111 -65
app.py CHANGED
@@ -2613,10 +2613,11 @@ with gr.Blocks(
2613
  def call_modal_batch_auto(user_id, time_period, max_txns, include_small):
2614
  """
2615
  Automatically fetch transactions and analyze with Modal
 
2616
  """
2617
  import time
2618
 
2619
- # βœ… FIX: Define days_map here (at function scope)
2620
  days_map = {
2621
  "Last 7 Days": 7,
2622
  "Last 30 Days": 30,
@@ -2629,14 +2630,14 @@ with gr.Blocks(
2629
  # Step 1: Show loading state
2630
  yield (
2631
  f"""
2632
- ## ⏳ Loading Transactions...
2633
-
2634
- **User:** {user_id}
2635
- **Period:** {time_period}
2636
- **Status:** Fetching transaction history...
2637
-
2638
- <div class="thinking-dots">Please wait</div>
2639
- """,
2640
  "",
2641
  None,
2642
  None
@@ -2661,13 +2662,13 @@ with gr.Blocks(
2661
  # Step 3: Show processing state
2662
  yield (
2663
  f"""
2664
- ## ⚑ Processing {len(transactions)} Transactions...
2665
-
2666
- **Status:** Calling Modal serverless endpoint
2667
- **Mode:** Parallel batch processing
2668
-
2669
- <div class="thinking-dots">Analyzing with AI</div>
2670
- """,
2671
  "",
2672
  None,
2673
  None
@@ -2700,13 +2701,30 @@ with gr.Blocks(
2700
  # Extract recommendation
2701
  rec = data.get('recommendation', data)
2702
 
 
2703
  card_id = rec.get('recommended_card', 'Unknown')
2704
- card_name = card_id.replace('c_', '').replace('_', ' ').title()
 
 
 
 
 
 
 
2705
  optimal_rewards = float(rec.get('rewards_earned', 0))
2706
 
2707
- # Estimate what they actually earned (assume 1% default)
 
 
 
 
 
2708
  actual_rewards = txn['amount'] * 0.01
2709
 
 
 
 
 
2710
  results.append({
2711
  'date': txn['date'],
2712
  'merchant': txn['merchant'],
@@ -2715,7 +2733,7 @@ with gr.Blocks(
2715
  'recommended_card': card_name,
2716
  'optimal_rewards': optimal_rewards,
2717
  'actual_rewards': actual_rewards,
2718
- 'missed_savings': optimal_rewards - actual_rewards
2719
  })
2720
 
2721
  total_rewards_earned += actual_rewards
@@ -2735,29 +2753,45 @@ with gr.Blocks(
2735
  )
2736
  return
2737
 
2738
- # Calculate metrics
2739
- total_missed = total_optimal_rewards - total_rewards_earned
2740
- avg_optimization = (total_optimal_rewards / total_spending * 100) if total_spending > 0 else 0
2741
- avg_actual = (total_rewards_earned / total_spending * 100) if total_spending > 0 else 0
 
 
 
 
 
 
 
 
 
 
 
 
2742
 
2743
  # Sort by missed savings (biggest opportunities first)
2744
  results.sort(key=lambda x: x['missed_savings'], reverse=True)
2745
 
2746
- # βœ… FIX: Use days_map from function scope
2747
  days = days_map.get(time_period, 30)
2748
 
 
 
 
 
2749
  # Format output
2750
  status_msg = f"""
2751
- ## βœ… Analysis Complete!
2752
-
2753
- **Transactions Analyzed:** {len(results)}
2754
- **Time Period:** {time_period}
2755
- **Processing Time:** ~{len(results) * 0.05:.1f}s (Modal parallel processing)
2756
- """
2757
 
2758
  output = f"""
2759
  ## πŸ’° Optimization Report for {user_id}
2760
-
2761
  ### πŸ“Š Summary
2762
 
2763
  | Metric | Value |
@@ -2778,16 +2812,27 @@ with gr.Blocks(
2778
  for rec in results[:10]:
2779
  output += f"| {rec['date']} | {rec['merchant']} | ${rec['amount']:.2f} | {rec['recommended_card']} | ${rec['missed_savings']:.2f} |\n"
2780
 
 
 
 
 
 
 
 
 
 
 
 
2781
  output += f"""
2782
-
2783
  ---
2784
 
2785
  ### πŸ’‘ Key Insights
2786
 
2787
- - **Biggest Single Opportunity:** ${max(results, key=lambda x: x['missed_savings'])['missed_savings']:.2f} at {max(results, key=lambda x: x['missed_savings'])['merchant']}
2788
- - **Most Common Category:** {max(set([r['category'] for r in results]), key=[r['category'] for r in results].count)}
2789
  - **Average Transaction:** ${total_spending / len(results):.2f}
2790
- - **Optimization Potential:** {((total_optimal_rewards - total_rewards_earned) / total_rewards_earned * 100):.1f}% more rewards possible
2791
 
2792
  ---
2793
 
@@ -2796,9 +2841,10 @@ with gr.Blocks(
2796
  <p style="margin: 0; color: #5d4037; font-size: 15px;">
2797
  If you had used our AI recommendations for these {len(results)} transactions, you would have earned
2798
  <strong style="color: #e65100;">${total_missed:.2f} more</strong> in rewards.
2799
- Over a full year, that's <strong style="color: #e65100;">${total_missed * (365 / days):.0f}+</strong> in extra rewards!
2800
  </p>
2801
  </div>
 
2802
  ---
2803
 
2804
  <div style="background: #e8f5e9; padding: 20px; border-radius: 10px; border-left: 4px solid #4caf50;">
@@ -2818,21 +2864,21 @@ with gr.Blocks(
2818
  merchant_data[r['merchant']]['optimal'] += r['optimal_rewards']
2819
  merchant_data[r['merchant']]['actual'] += r['actual_rewards']
2820
 
2821
- top_merchants = sorted(merchant_data.items(), key=lambda x: x[1]['optimal'], reverse=True)[:10]
2822
 
2823
  fig1 = go.Figure()
2824
 
2825
  fig1.add_trace(go.Bar(
2826
  name='Optimal (with AI)',
2827
  x=[m[0] for m in top_merchants],
2828
- y=[m[1]['optimal'] for m in top_merchants],
2829
  marker_color='#4caf50'
2830
  ))
2831
 
2832
  fig1.add_trace(go.Bar(
2833
  name='Actual (what you earned)',
2834
  x=[m[0] for m in top_merchants],
2835
- y=[m[1]['actual'] for m in top_merchants],
2836
  marker_color='#ff9800'
2837
  ))
2838
 
@@ -2926,32 +2972,32 @@ with gr.Blocks(
2926
  )
2927
 
2928
  gr.Markdown("""
2929
- ---
2930
-
2931
- ### πŸ”§ How Modal Powers This
2932
-
2933
- **Traditional Approach:**
2934
- - Process 50 transactions sequentially
2935
- - Takes 50 Γ— 2 seconds = **100 seconds**
2936
- - Server must handle all load
2937
-
2938
- **With Modal:**
2939
- - Process 50 transactions in parallel
2940
- - Takes **~3 seconds total**
2941
- - Automatic scaling (0 to 100 containers instantly)
2942
- - Pay only for compute time used
2943
-
2944
- **Architecture:**
2945
- ```
2946
- Gradio UI β†’ Modal Endpoint β†’ [Container 1, Container 2, ..., Container N]
2947
- ↓
2948
- Your Orchestrator API
2949
- ↓
2950
- Aggregated Results
2951
- ```
2952
-
2953
- **Learn More:** [Modal Documentation](https://modal.com/docs)
2954
- """)
2955
 
2956
  # ==================== TAB: ASK AI (WITH VOICE) ====================
2957
  with gr.Tab("πŸ’¬ Ask AI"):
 
2613
  def call_modal_batch_auto(user_id, time_period, max_txns, include_small):
2614
  """
2615
  Automatically fetch transactions and analyze with Modal
2616
+ βœ… FIXED VERSION - Correct calculation logic
2617
  """
2618
  import time
2619
 
2620
+ # βœ… FIX 1: Define days_map at function scope
2621
  days_map = {
2622
  "Last 7 Days": 7,
2623
  "Last 30 Days": 30,
 
2630
  # Step 1: Show loading state
2631
  yield (
2632
  f"""
2633
+ ## ⏳ Loading Transactions...
2634
+
2635
+ **User:** {user_id}
2636
+ **Period:** {time_period}
2637
+ **Status:** Fetching transaction history...
2638
+
2639
+ <div class="thinking-dots">Please wait</div>
2640
+ """,
2641
  "",
2642
  None,
2643
  None
 
2662
  # Step 3: Show processing state
2663
  yield (
2664
  f"""
2665
+ ## ⚑ Processing {len(transactions)} Transactions...
2666
+
2667
+ **Status:** Calling Modal serverless endpoint
2668
+ **Mode:** Parallel batch processing
2669
+
2670
+ <div class="thinking-dots">Analyzing with AI</div>
2671
+ """,
2672
  "",
2673
  None,
2674
  None
 
2701
  # Extract recommendation
2702
  rec = data.get('recommendation', data)
2703
 
2704
+ # βœ… FIX 2: Extract card name properly
2705
  card_id = rec.get('recommended_card', 'Unknown')
2706
+
2707
+ # Better card name formatting
2708
+ if card_id.startswith('c_'):
2709
+ card_name = card_id[2:].replace('_', ' ').title()
2710
+ else:
2711
+ card_name = card_id.replace('_', ' ').title()
2712
+
2713
+ # βœ… FIX 3: Get optimal rewards correctly
2714
  optimal_rewards = float(rec.get('rewards_earned', 0))
2715
 
2716
+ # If rewards_earned is missing, calculate from rate
2717
+ if optimal_rewards == 0:
2718
+ reward_rate = float(rec.get('reward_rate', 0.01))
2719
+ optimal_rewards = txn['amount'] * reward_rate
2720
+
2721
+ # Estimate what they actually earned (assume 1% default card)
2722
  actual_rewards = txn['amount'] * 0.01
2723
 
2724
+ # βœ… FIX 4: Calculate missed savings CORRECTLY
2725
+ # Missed savings = what you COULD have earned - what you DID earn
2726
+ missed_savings = optimal_rewards - actual_rewards
2727
+
2728
  results.append({
2729
  'date': txn['date'],
2730
  'merchant': txn['merchant'],
 
2733
  'recommended_card': card_name,
2734
  'optimal_rewards': optimal_rewards,
2735
  'actual_rewards': actual_rewards,
2736
+ 'missed_savings': missed_savings # Should be POSITIVE
2737
  })
2738
 
2739
  total_rewards_earned += actual_rewards
 
2753
  )
2754
  return
2755
 
2756
+ # βœ… FIX 5: Calculate metrics CORRECTLY
2757
+ total_missed = total_optimal_rewards - total_rewards_earned # Should be POSITIVE
2758
+
2759
+ # Avoid division by zero
2760
+ if total_spending > 0:
2761
+ avg_optimization = (total_optimal_rewards / total_spending * 100)
2762
+ avg_actual = (total_rewards_earned / total_spending * 100)
2763
+ else:
2764
+ avg_optimization = 0
2765
+ avg_actual = 0
2766
+
2767
+ # βœ… FIX 6: Optimization potential calculation
2768
+ if total_rewards_earned > 0:
2769
+ optimization_potential = ((total_optimal_rewards - total_rewards_earned) / total_rewards_earned * 100)
2770
+ else:
2771
+ optimization_potential = 0
2772
 
2773
  # Sort by missed savings (biggest opportunities first)
2774
  results.sort(key=lambda x: x['missed_savings'], reverse=True)
2775
 
2776
+ # Get days for yearly projection
2777
  days = days_map.get(time_period, 30)
2778
 
2779
+ # βœ… FIX 7: Yearly projection
2780
+ yearly_multiplier = 365 / days if days > 0 else 12
2781
+ yearly_projection = total_missed * yearly_multiplier
2782
+
2783
  # Format output
2784
  status_msg = f"""
2785
+ ## βœ… Analysis Complete!
2786
+
2787
+ **Transactions Analyzed:** {len(results)}
2788
+ **Time Period:** {time_period}
2789
+ **Processing Time:** ~{len(results) * 0.05:.1f}s (Modal parallel processing)
2790
+ """
2791
 
2792
  output = f"""
2793
  ## πŸ’° Optimization Report for {user_id}
2794
+
2795
  ### πŸ“Š Summary
2796
 
2797
  | Metric | Value |
 
2812
  for rec in results[:10]:
2813
  output += f"| {rec['date']} | {rec['merchant']} | ${rec['amount']:.2f} | {rec['recommended_card']} | ${rec['missed_savings']:.2f} |\n"
2814
 
2815
+ # Find most common category safely
2816
+ category_counts = {}
2817
+ for r in results:
2818
+ cat = r['category']
2819
+ category_counts[cat] = category_counts.get(cat, 0) + 1
2820
+
2821
+ most_common_category = max(category_counts.items(), key=lambda x: x[0])[0] if category_counts else "Unknown"
2822
+
2823
+ # Find biggest opportunity
2824
+ biggest_opp = max(results, key=lambda x: x['missed_savings'])
2825
+
2826
  output += f"""
2827
+
2828
  ---
2829
 
2830
  ### πŸ’‘ Key Insights
2831
 
2832
+ - **Biggest Single Opportunity:** ${biggest_opp['missed_savings']:.2f} at {biggest_opp['merchant']}
2833
+ - **Most Common Category:** {most_common_category}
2834
  - **Average Transaction:** ${total_spending / len(results):.2f}
2835
+ - **Optimization Potential:** +{optimization_potential:.1f}% more rewards possible
2836
 
2837
  ---
2838
 
 
2841
  <p style="margin: 0; color: #5d4037; font-size: 15px;">
2842
  If you had used our AI recommendations for these {len(results)} transactions, you would have earned
2843
  <strong style="color: #e65100;">${total_missed:.2f} more</strong> in rewards.
2844
+ Over a full year, that's <strong style="color: #e65100;">${yearly_projection:.0f}+</strong> in extra rewards!
2845
  </p>
2846
  </div>
2847
+
2848
  ---
2849
 
2850
  <div style="background: #e8f5e9; padding: 20px; border-radius: 10px; border-left: 4px solid #4caf50;">
 
2864
  merchant_data[r['merchant']]['optimal'] += r['optimal_rewards']
2865
  merchant_data[r['merchant']]['actual'] += r['actual_rewards']
2866
 
2867
+ top_merchants = sorted(merchant_data.items(), key=lambda x: x[0]['optimal'], reverse=True)[:10]
2868
 
2869
  fig1 = go.Figure()
2870
 
2871
  fig1.add_trace(go.Bar(
2872
  name='Optimal (with AI)',
2873
  x=[m[0] for m in top_merchants],
2874
+ y=[m[0]['optimal'] for m in top_merchants],
2875
  marker_color='#4caf50'
2876
  ))
2877
 
2878
  fig1.add_trace(go.Bar(
2879
  name='Actual (what you earned)',
2880
  x=[m[0] for m in top_merchants],
2881
+ y=[m[0]['actual'] for m in top_merchants],
2882
  marker_color='#ff9800'
2883
  ))
2884
 
 
2972
  )
2973
 
2974
  gr.Markdown("""
2975
+ ---
2976
+
2977
+ ### πŸ”§ How Modal Powers This
2978
+
2979
+ **Traditional Approach:**
2980
+ - Process 50 transactions sequentially
2981
+ - Takes 50 Γ— 2 seconds = **100 seconds**
2982
+ - Server must handle all load
2983
+
2984
+ **With Modal:**
2985
+ - Process 50 transactions in parallel
2986
+ - Takes **~3 seconds total**
2987
+ - Automatic scaling (0 to 100 containers instantly)
2988
+ - Pay only for compute time used
2989
+
2990
+ **Architecture:**
2991
+ ```
2992
+ Gradio UI β†’ Modal Endpoint β†’ [Container 1, Container 2, ..., Container N]
2993
+ ↓
2994
+ Your Orchestrator API
2995
+ ↓
2996
+ Aggregated Results
2997
+ ```
2998
+
2999
+ **Learn More:** [Modal Documentation](https://modal.com/docs)
3000
+ """)
3001
 
3002
  # ==================== TAB: ASK AI (WITH VOICE) ====================
3003
  with gr.Tab("πŸ’¬ Ask AI"):