Update app.py
Browse files
app.py
CHANGED
|
@@ -3141,12 +3141,11 @@ with gr.Blocks(
|
|
| 3141 |
return "β Please upload a receipt image first.", None
|
| 3142 |
|
| 3143 |
try:
|
| 3144 |
-
# Read and encode image
|
| 3145 |
import base64
|
| 3146 |
with open(image_path, "rb") as image_file:
|
| 3147 |
base64_image = base64.b64encode(image_file.read()).decode('utf-8')
|
| 3148 |
|
| 3149 |
-
# β
|
| 3150 |
response = openai_client.chat.completions.create(
|
| 3151 |
model="gpt-4o",
|
| 3152 |
messages=[{
|
|
@@ -3154,22 +3153,39 @@ with gr.Blocks(
|
|
| 3154 |
"content": [
|
| 3155 |
{
|
| 3156 |
"type": "text",
|
| 3157 |
-
"text": """Extract the following from this receipt:
|
| 3158 |
-
|
| 3159 |
-
|
| 3160 |
-
|
| 3161 |
-
|
| 3162 |
-
|
| 3163 |
-
|
| 3164 |
-
|
| 3165 |
-
|
| 3166 |
-
|
| 3167 |
-
|
| 3168 |
-
|
| 3169 |
-
|
| 3170 |
-
|
| 3171 |
-
|
| 3172 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3173 |
},
|
| 3174 |
{
|
| 3175 |
"type": "image_url",
|
|
@@ -3185,7 +3201,6 @@ with gr.Blocks(
|
|
| 3185 |
# Parse response
|
| 3186 |
receipt_data_str = response.choices[0].message.content
|
| 3187 |
|
| 3188 |
-
# Extract JSON from response (GPT sometimes adds extra text)
|
| 3189 |
import re
|
| 3190 |
json_match = re.search(r'\{.*\}', receipt_data_str, re.DOTALL)
|
| 3191 |
if json_match:
|
|
@@ -3193,50 +3208,88 @@ with gr.Blocks(
|
|
| 3193 |
else:
|
| 3194 |
raise ValueError("Could not extract JSON from response")
|
| 3195 |
|
| 3196 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3197 |
rec_result = client.get_recommendation(
|
| 3198 |
user_id=user_id,
|
| 3199 |
merchant=receipt_data['merchant'],
|
| 3200 |
-
category=
|
| 3201 |
amount=float(receipt_data['amount']),
|
| 3202 |
-
mcc=
|
| 3203 |
)
|
| 3204 |
|
| 3205 |
if rec_result.get('success'):
|
| 3206 |
data = normalize_recommendation_data(rec_result.get('data', {}))
|
| 3207 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3208 |
output = f"""## πΈ Receipt Analysis
|
| 3209 |
-
|
| 3210 |
-
|
| 3211 |
-
|
| 3212 |
-
|
| 3213 |
-
|
| 3214 |
-
|
| 3215 |
-
|
| 3216 |
-
|
| 3217 |
-
|
| 3218 |
for item in receipt_data.get('items', []):
|
| 3219 |
output += f"- {item}\n"
|
| 3220 |
|
| 3221 |
output += f"""
|
| 3222 |
-
|
| 3223 |
-
|
| 3224 |
-
|
| 3225 |
-
|
| 3226 |
-
|
| 3227 |
-
|
| 3228 |
-
|
| 3229 |
-
|
| 3230 |
-
|
| 3231 |
-
|
| 3232 |
-
|
| 3233 |
-
|
|
|
|
|
|
|
| 3234 |
|
| 3235 |
if data['warnings']:
|
| 3236 |
output += "### β οΈ Warnings\n\n"
|
| 3237 |
for warning in data['warnings']:
|
| 3238 |
output += f"- {warning}\n"
|
| 3239 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3240 |
# Create chart
|
| 3241 |
chart = create_rewards_comparison_chart(data)
|
| 3242 |
|
|
|
|
| 3141 |
return "β Please upload a receipt image first.", None
|
| 3142 |
|
| 3143 |
try:
|
|
|
|
| 3144 |
import base64
|
| 3145 |
with open(image_path, "rb") as image_file:
|
| 3146 |
base64_image = base64.b64encode(image_file.read()).decode('utf-8')
|
| 3147 |
|
| 3148 |
+
# β
IMPROVED PROMPT with better category detection
|
| 3149 |
response = openai_client.chat.completions.create(
|
| 3150 |
model="gpt-4o",
|
| 3151 |
messages=[{
|
|
|
|
| 3153 |
"content": [
|
| 3154 |
{
|
| 3155 |
"type": "text",
|
| 3156 |
+
"text": """Extract the following from this receipt and classify accurately:
|
| 3157 |
+
|
| 3158 |
+
1. **Merchant name** (exact as shown on receipt)
|
| 3159 |
+
2. **Total amount** (final total only, not subtotals)
|
| 3160 |
+
3. **Date** (format: YYYY-MM-DD, or "Unknown" if not visible)
|
| 3161 |
+
4. **Category** - Choose the MOST SPECIFIC category:
|
| 3162 |
+
- "Wholesale Club" (Costco, Sam's Club, BJ's)
|
| 3163 |
+
- "Grocery Store" (Whole Foods, Safeway, Kroger, Trader Joe's)
|
| 3164 |
+
- "Supermarket" (traditional grocery stores)
|
| 3165 |
+
- "Convenience Store" (7-Eleven, Circle K)
|
| 3166 |
+
- "Restaurant" (sit-down dining)
|
| 3167 |
+
- "Fast Food" (quick service)
|
| 3168 |
+
- "Gas Station"
|
| 3169 |
+
- "Department Store" (Target, Walmart)
|
| 3170 |
+
- "Online Shopping"
|
| 3171 |
+
- "Other"
|
| 3172 |
+
|
| 3173 |
+
5. **Top 3 items** purchased (if visible)
|
| 3174 |
+
|
| 3175 |
+
**IMPORTANT RULES:**
|
| 3176 |
+
- Costco, Sam's Club, BJ's = "Wholesale Club" (NOT "Grocery Store")
|
| 3177 |
+
- Walmart Supercenter = "Department Store" (NOT "Grocery Store")
|
| 3178 |
+
- Target = "Department Store" (NOT "Grocery Store")
|
| 3179 |
+
- Whole Foods, Safeway, Kroger = "Grocery Store"
|
| 3180 |
+
|
| 3181 |
+
Return as JSON:
|
| 3182 |
+
{
|
| 3183 |
+
"merchant": "Store Name",
|
| 3184 |
+
"amount": 127.50,
|
| 3185 |
+
"date": "2025-01-28",
|
| 3186 |
+
"category": "Wholesale Club",
|
| 3187 |
+
"items": ["Item 1", "Item 2", "Item 3"]
|
| 3188 |
+
}"""
|
| 3189 |
},
|
| 3190 |
{
|
| 3191 |
"type": "image_url",
|
|
|
|
| 3201 |
# Parse response
|
| 3202 |
receipt_data_str = response.choices[0].message.content
|
| 3203 |
|
|
|
|
| 3204 |
import re
|
| 3205 |
json_match = re.search(r'\{.*\}', receipt_data_str, re.DOTALL)
|
| 3206 |
if json_match:
|
|
|
|
| 3208 |
else:
|
| 3209 |
raise ValueError("Could not extract JSON from response")
|
| 3210 |
|
| 3211 |
+
category = receipt_data['category']
|
| 3212 |
+
|
| 3213 |
+
# Map category to correct MCC
|
| 3214 |
+
category_to_mcc = {
|
| 3215 |
+
"Wholesale Club": "5300",
|
| 3216 |
+
"Grocery Store": "5411",
|
| 3217 |
+
"Supermarket": "5411",
|
| 3218 |
+
"Convenience Store": "5499",
|
| 3219 |
+
"Restaurant": "5812",
|
| 3220 |
+
"Fast Food": "5814",
|
| 3221 |
+
"Gas Station": "5541",
|
| 3222 |
+
"Department Store": "5311",
|
| 3223 |
+
"Online Shopping": "5942"
|
| 3224 |
+
}
|
| 3225 |
+
|
| 3226 |
+
mcc = category_to_mcc.get(category, MCC_CATEGORIES.get(category, "5999"))
|
| 3227 |
+
|
| 3228 |
+
print(f"π Receipt Analysis:")
|
| 3229 |
+
print(f" Merchant: {receipt_data['merchant']}")
|
| 3230 |
+
print(f" Category: {category}")
|
| 3231 |
+
print(f" MCC: {mcc}")
|
| 3232 |
+
print(f" Amount: ${receipt_data['amount']:.2f}")
|
| 3233 |
+
|
| 3234 |
+
# Get card recommendation with correct MCC
|
| 3235 |
rec_result = client.get_recommendation(
|
| 3236 |
user_id=user_id,
|
| 3237 |
merchant=receipt_data['merchant'],
|
| 3238 |
+
category=category, # Use detected category
|
| 3239 |
amount=float(receipt_data['amount']),
|
| 3240 |
+
mcc=mcc # Use correct MCC
|
| 3241 |
)
|
| 3242 |
|
| 3243 |
if rec_result.get('success'):
|
| 3244 |
data = normalize_recommendation_data(rec_result.get('data', {}))
|
| 3245 |
|
| 3246 |
+
# β
Add helpful context for special cases
|
| 3247 |
+
context_note = ""
|
| 3248 |
+
if "costco" in receipt_data['merchant'].lower():
|
| 3249 |
+
context_note = """
|
| 3250 |
+
> **π‘ Costco Tip:** Costco only accepts Visa cards. Amex and Mastercard are not accepted at warehouse locations.
|
| 3251 |
+
> The Costco Anywhere Visa Card offers 2% cashback at Costco and Costco.com.
|
| 3252 |
+
"""
|
| 3253 |
+
|
| 3254 |
output = f"""## πΈ Receipt Analysis
|
| 3255 |
+
|
| 3256 |
+
### π§Ύ Extracted Information
|
| 3257 |
+
- **Merchant:** {receipt_data['merchant']}
|
| 3258 |
+
- **Amount:** ${receipt_data['amount']:.2f}
|
| 3259 |
+
- **Date:** {receipt_data['date']}
|
| 3260 |
+
- **Category:** {receipt_data['category']} (MCC: {mcc})
|
| 3261 |
+
|
| 3262 |
+
**Items Purchased:**
|
| 3263 |
+
"""
|
| 3264 |
for item in receipt_data.get('items', []):
|
| 3265 |
output += f"- {item}\n"
|
| 3266 |
|
| 3267 |
output += f"""
|
| 3268 |
+
|
| 3269 |
+
{context_note}
|
| 3270 |
+
|
| 3271 |
+
---
|
| 3272 |
+
|
| 3273 |
+
### π³ Optimal Card Recommendation
|
| 3274 |
+
|
| 3275 |
+
**Use: {data['recommended_card']}**
|
| 3276 |
+
|
| 3277 |
+
- **Rewards Earned:** ${data['rewards_earned']:.2f} ({data['rewards_rate']})
|
| 3278 |
+
- **Reasoning:** {data['reasoning']}
|
| 3279 |
+
- **Annual Potential:** ${data['annual_potential']:.2f}/year
|
| 3280 |
+
|
| 3281 |
+
"""
|
| 3282 |
|
| 3283 |
if data['warnings']:
|
| 3284 |
output += "### β οΈ Warnings\n\n"
|
| 3285 |
for warning in data['warnings']:
|
| 3286 |
output += f"- {warning}\n"
|
| 3287 |
|
| 3288 |
+
if data.get('alternatives'):
|
| 3289 |
+
output += "\n### π Alternative Options\n\n"
|
| 3290 |
+
for alt in data['alternatives'][:3]:
|
| 3291 |
+
output += f"- **{alt['card']}:** ${alt['rewards']:.2f} ({alt['rate']})\n"
|
| 3292 |
+
|
| 3293 |
# Create chart
|
| 3294 |
chart = create_rewards_comparison_chart(data)
|
| 3295 |
|