zarashahid commited on
Commit
79bf10e
Β·
verified Β·
1 Parent(s): 6c28752

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +628 -0
app.py ADDED
@@ -0,0 +1,628 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Smart Shopping Agent for Hugging Face Spaces
2
+ # File: app.py
3
+
4
+ import streamlit as st
5
+ import pandas as pd
6
+ import requests
7
+ from bs4 import BeautifulSoup
8
+ import json
9
+ import re
10
+ from typing import List, Dict, Any
11
+ import time
12
+ from dataclasses import dataclass
13
+ import os
14
+ import random
15
+ from datetime import datetime
16
+
17
+ # Configure Streamlit page
18
+ st.set_page_config(
19
+ page_title="Smart Shopping Agent",
20
+ page_icon="πŸ›’",
21
+ layout="wide",
22
+ initial_sidebar_state="expanded"
23
+ )
24
+
25
+ # Product data structure
26
+ @dataclass
27
+ class Product:
28
+ name: str
29
+ price: float
30
+ rating: float
31
+ specs: Dict[str, Any]
32
+ source: str
33
+ url: str = ""
34
+
35
+ class ShoppingAgent:
36
+ """Main shopping agent class that handles the complete workflow"""
37
+
38
+ def __init__(self):
39
+ self.search_history = []
40
+
41
+ def search_products(self, query: str) -> List[Dict]:
42
+ """Search for products - using enhanced mock data for demo"""
43
+ try:
44
+ # Simulate realistic product search
45
+ products = self._enhanced_mock_search(query)
46
+ return products
47
+ except Exception as e:
48
+ st.error(f"Error searching products: {e}")
49
+ return []
50
+
51
+ def _enhanced_mock_search(self, query: str) -> List[Dict]:
52
+ """Enhanced mock search with realistic product data"""
53
+
54
+ # Product categories and their typical features
55
+ category_specs = {
56
+ 'headphones': ['wireless', 'noise_cancelling', 'battery_life', 'driver_size'],
57
+ 'laptop': ['processor', 'ram', 'storage', 'screen_size', 'graphics'],
58
+ 'smartphone': ['camera_mp', 'battery_mah', 'storage_gb', 'screen_inches', 'processor'],
59
+ 'smartwatch': ['battery_days', 'water_resistance', 'health_sensors', 'compatibility'],
60
+ 'tablet': ['screen_size', 'storage', 'battery_hours', 'processor', 'weight'],
61
+ 'speaker': ['power_watts', 'battery_hours', 'water_rating', 'connectivity']
62
+ }
63
+
64
+ # Determine category based on query
65
+ category = 'general'
66
+ for cat in category_specs.keys():
67
+ if cat in query.lower():
68
+ category = cat
69
+ break
70
+
71
+ # Generate realistic products
72
+ products = []
73
+ brand_pools = {
74
+ 'headphones': ['Sony', 'Bose', 'Apple', 'Samsung', 'JBL', 'Audio-Technica', 'Sennheiser'],
75
+ 'laptop': ['Apple', 'Dell', 'HP', 'Lenovo', 'ASUS', 'Acer', 'MSI'],
76
+ 'smartphone': ['Apple', 'Samsung', 'Google', 'OnePlus', 'Xiaomi', 'Huawei'],
77
+ 'smartwatch': ['Apple', 'Samsung', 'Fitbit', 'Garmin', 'Amazfit', 'Fossil'],
78
+ 'tablet': ['Apple', 'Samsung', 'Microsoft', 'Amazon', 'Lenovo'],
79
+ 'speaker': ['JBL', 'Bose', 'Sony', 'Ultimate Ears', 'Marshall', 'Harman Kardon']
80
+ }
81
+
82
+ brands = brand_pools.get(category, ['BrandA', 'BrandB', 'BrandC', 'BrandD', 'BrandE'])
83
+ stores = ['Amazon', 'Best Buy', 'Target', 'Walmart', 'Newegg', 'B&H Photo']
84
+
85
+ for i in range(8): # Generate 8 products
86
+ brand = random.choice(brands)
87
+ model_suffix = random.choice(['Pro', 'Max', 'Ultra', 'Plus', 'Air', 'Mini', 'SE', 'Standard'])
88
+
89
+ # Generate realistic specs based on category
90
+ specs = {'brand': brand}
91
+ if category in category_specs:
92
+ for spec in category_specs[category]:
93
+ if spec == 'battery_life':
94
+ specs[spec] = f"{random.randint(20, 40)} hours"
95
+ elif spec == 'driver_size':
96
+ specs[spec] = f"{random.randint(40, 50)}mm"
97
+ elif spec == 'processor':
98
+ processors = ['Intel i5', 'Intel i7', 'AMD Ryzen 5', 'AMD Ryzen 7', 'Apple M1', 'Apple M2']
99
+ specs[spec] = random.choice(processors)
100
+ elif spec == 'ram':
101
+ specs[spec] = f"{random.choice([8, 16, 32])}GB"
102
+ elif spec == 'storage':
103
+ specs[spec] = f"{random.choice([256, 512, 1000])}GB SSD"
104
+ elif spec == 'camera_mp':
105
+ specs[spec] = f"{random.randint(48, 108)}MP"
106
+ else:
107
+ specs[spec] = f"High Quality {spec.replace('_', ' ').title()}"
108
+
109
+ # Add common specs
110
+ specs.update({
111
+ 'warranty': f"{random.randint(1, 3)} year(s)",
112
+ 'availability': random.choice(['In Stock', 'Limited Stock', '2-3 days shipping']),
113
+ 'color_options': random.choice(['Black, White', 'Multiple Colors', 'Black, Silver, Gold'])
114
+ })
115
+
116
+ # Generate realistic pricing based on brand and category
117
+ base_prices = {
118
+ 'headphones': (50, 400),
119
+ 'laptop': (400, 2500),
120
+ 'smartphone': (200, 1200),
121
+ 'smartwatch': (100, 800),
122
+ 'tablet': (150, 1000),
123
+ 'speaker': (30, 300)
124
+ }
125
+
126
+ price_range = base_prices.get(category, (50, 500))
127
+ base_price = random.uniform(price_range[0], price_range[1])
128
+
129
+ # Premium brands cost more
130
+ if brand in ['Apple', 'Sony', 'Bose']:
131
+ base_price *= 1.3
132
+
133
+ product = {
134
+ 'name': f"{brand} {query.title()} {model_suffix}",
135
+ 'price': round(base_price, 2),
136
+ 'rating': round(random.uniform(3.2, 4.9), 1),
137
+ 'specs': specs,
138
+ 'source': random.choice(stores),
139
+ 'url': f"https://{random.choice(stores).lower().replace(' ', '')}.com/product/{query.replace(' ', '-')}-{i}",
140
+ 'reviews_count': random.randint(50, 2000),
141
+ 'shipping': random.choice(['Free shipping', '$5.99 shipping', 'Prime eligible'])
142
+ }
143
+ products.append(product)
144
+
145
+ # Sort by a mix of rating and randomness for realistic results
146
+ products.sort(key=lambda x: x['rating'] + random.uniform(-0.3, 0.3), reverse=True)
147
+ return products
148
+
149
+ def compare_products(self, products: List[Dict]) -> Dict:
150
+ """Compare products and generate analysis"""
151
+ if not products:
152
+ return {}
153
+
154
+ try:
155
+ comparison = {
156
+ 'total_products': len(products),
157
+ 'search_timestamp': datetime.now().isoformat(),
158
+ 'price_analysis': {
159
+ 'min': min(p['price'] for p in products),
160
+ 'max': max(p['price'] for p in products),
161
+ 'average': round(sum(p['price'] for p in products) / len(products), 2),
162
+ 'median': round(sorted([p['price'] for p in products])[len(products)//2], 2)
163
+ },
164
+ 'rating_analysis': {
165
+ 'min': min(p['rating'] for p in products),
166
+ 'max': max(p['rating'] for p in products),
167
+ 'average': round(sum(p['rating'] for p in products) / len(products), 1),
168
+ 'median': round(sorted([p['rating'] for p in products])[len(products)//2], 1)
169
+ },
170
+ 'store_distribution': {},
171
+ 'brand_distribution': {},
172
+ 'products': products
173
+ }
174
+
175
+ # Store distribution
176
+ for product in products:
177
+ store = product['source']
178
+ comparison['store_distribution'][store] = comparison['store_distribution'].get(store, 0) + 1
179
+
180
+ # Brand distribution
181
+ for product in products:
182
+ brand = product['specs'].get('brand', 'Unknown')
183
+ comparison['brand_distribution'][brand] = comparison['brand_distribution'].get(brand, 0) + 1
184
+
185
+ return comparison
186
+
187
+ except Exception as e:
188
+ st.error(f"Error in product comparison: {e}")
189
+ return {}
190
+
191
+ def generate_recommendations(self, comparison: Dict, preferences: str = "") -> Dict:
192
+ """Generate smart recommendations based on analysis"""
193
+ if not comparison or not comparison['products']:
194
+ return {}
195
+
196
+ try:
197
+ products = comparison['products']
198
+
199
+ # Enhanced scoring algorithm
200
+ scored_products = []
201
+ for product in products:
202
+ # Normalize scores (0-1 range)
203
+ price_score = 1 - ((product['price'] - comparison['price_analysis']['min']) /
204
+ (comparison['price_analysis']['max'] - comparison['price_analysis']['min']))
205
+ rating_score = ((product['rating'] - comparison['rating_analysis']['min']) /
206
+ (comparison['rating_analysis']['max'] - comparison['rating_analysis']['min']))
207
+
208
+ # Reviews count influence (more reviews = more reliable)
209
+ reviews_score = min(product.get('reviews_count', 0) / 1000, 1.0) # Cap at 1000 reviews
210
+
211
+ # Preference-based scoring
212
+ preference_score = 0
213
+ if preferences:
214
+ pref_lower = preferences.lower()
215
+ # Brand preference
216
+ if product['specs'].get('brand', '').lower() in pref_lower:
217
+ preference_score += 0.2
218
+ # Budget preference
219
+ if 'budget' in pref_lower and product['price'] < comparison['price_analysis']['average']:
220
+ preference_score += 0.15
221
+ # Quality preference
222
+ if 'quality' in pref_lower or 'best' in pref_lower:
223
+ preference_score += (rating_score * 0.1)
224
+
225
+ # Composite score with weights
226
+ composite_score = (
227
+ price_score * 0.30 + # 30% price importance
228
+ rating_score * 0.40 + # 40% rating importance
229
+ reviews_score * 0.15 + # 15% reviews reliability
230
+ preference_score * 0.15 # 15% preference matching
231
+ )
232
+
233
+ scored_products.append({
234
+ **product,
235
+ 'composite_score': round(composite_score, 3),
236
+ 'price_score': round(price_score, 3),
237
+ 'rating_score': round(rating_score, 3),
238
+ 'reviews_score': round(reviews_score, 3),
239
+ 'preference_score': round(preference_score, 3)
240
+ })
241
+
242
+ # Sort by composite score
243
+ scored_products.sort(key=lambda x: x['composite_score'], reverse=True)
244
+
245
+ # Find category winners
246
+ best_price = min(products, key=lambda x: x['price'])
247
+ highest_rated = max(products, key=lambda x: x['rating'])
248
+ most_reviewed = max(products, key=lambda x: x.get('reviews_count', 0))
249
+
250
+ recommendations = {
251
+ 'best_overall': scored_products[0],
252
+ 'best_value': best_price,
253
+ 'highest_rated': highest_rated,
254
+ 'most_reviewed': most_reviewed,
255
+ 'top_3': scored_products[:3],
256
+ 'budget_options': [p for p in scored_products if p['price'] < comparison['price_analysis']['average']][:2],
257
+ 'premium_options': [p for p in scored_products if p['price'] > comparison['price_analysis']['average']][:2]
258
+ }
259
+
260
+ return recommendations
261
+
262
+ except Exception as e:
263
+ st.error(f"Error generating recommendations: {e}")
264
+ return {}
265
+
266
+ # Initialize the shopping agent
267
+ @st.cache_resource
268
+ def get_shopping_agent():
269
+ return ShoppingAgent()
270
+
271
+ # Main Streamlit UI
272
+ def main():
273
+ st.title("πŸ›’ Smart Shopping Agent")
274
+ st.markdown("### Your AI-powered shopping assistant that finds, compares, and recommends the best products!")
275
+
276
+ # Initialize agent
277
+ agent = get_shopping_agent()
278
+
279
+ # Sidebar
280
+ with st.sidebar:
281
+ st.header("πŸ”§ Agent Info")
282
+ st.markdown("**Platform:** Hugging Face Spaces")
283
+ st.markdown("**Framework:** Custom Multi-Agent System")
284
+ st.markdown("**UI:** Streamlit")
285
+ st.markdown("**Status:** βœ… Online")
286
+
287
+ st.header("🎯 How it works")
288
+ st.markdown("""
289
+ 1. **πŸ” Search**: Find products across multiple stores
290
+ 2. **πŸ“Š Compare**: Analyze prices, ratings, and features
291
+ 3. **πŸ† Recommend**: Get personalized suggestions
292
+ 4. **πŸ“ˆ Insights**: Market analysis and trends
293
+ """)
294
+
295
+ st.header("🌟 Features")
296
+ st.markdown("""
297
+ - Multi-store price comparison
298
+ - Smart recommendation scoring
299
+ - Detailed product specifications
300
+ - Market trend analysis
301
+ - Export comparison reports
302
+ """)
303
+
304
+ # Main search interface
305
+ st.header("πŸ” Product Search")
306
+
307
+ col1, col2 = st.columns([3, 1])
308
+
309
+ with col1:
310
+ product_query = st.text_input(
311
+ "What product are you looking for?",
312
+ placeholder="e.g., wireless headphones, gaming laptop, smartphone",
313
+ key="product_search"
314
+ )
315
+
316
+ with col2:
317
+ st.markdown("**Popular Searches:**")
318
+ popular_searches = [
319
+ "wireless headphones",
320
+ "gaming laptop",
321
+ "smartphone",
322
+ "smartwatch",
323
+ "bluetooth speaker"
324
+ ]
325
+
326
+ for search in popular_searches:
327
+ if st.button(f"πŸ” {search}", key=f"popular_{search}"):
328
+ st.session_state.product_search = search
329
+ st.experimental_rerun()
330
+
331
+ # Preferences section
332
+ with st.expander("βš™οΈ Search Preferences (Optional)", expanded=False):
333
+ col1, col2, col3 = st.columns(3)
334
+
335
+ with col1:
336
+ budget_range = st.selectbox(
337
+ "Budget Range",
338
+ ["Any Budget", "Under $100", "$100-$500", "$500-$1000", "Over $1000"]
339
+ )
340
+
341
+ with col2:
342
+ brand_preference = st.text_input(
343
+ "Preferred Brands",
344
+ placeholder="e.g., Apple, Sony, Samsung"
345
+ )
346
+
347
+ with col3:
348
+ priority = st.selectbox(
349
+ "Priority",
350
+ ["Best Overall", "Best Value", "Highest Quality", "Most Popular"]
351
+ )
352
+
353
+ preferences = f"Budget: {budget_range}. Brands: {brand_preference}. Priority: {priority}."
354
+
355
+ # Search button
356
+ if st.button("πŸš€ Find Best Products", type="primary", use_container_width=True):
357
+ if product_query:
358
+ # Store search in session state
359
+ if 'search_results' not in st.session_state:
360
+ st.session_state.search_results = {}
361
+
362
+ # Progress indicator
363
+ progress_container = st.container()
364
+ with progress_container:
365
+ progress_bar = st.progress(0)
366
+ status_text = st.empty()
367
+
368
+ # Step 1: Search
369
+ status_text.text("πŸ” Searching for products...")
370
+ progress_bar.progress(25)
371
+ products = agent.search_products(product_query)
372
+
373
+ # Step 2: Compare
374
+ status_text.text("πŸ“Š Comparing features and prices...")
375
+ progress_bar.progress(50)
376
+ comparison = agent.compare_products(products)
377
+
378
+ # Step 3: Generate recommendations
379
+ status_text.text("🎯 Generating personalized recommendations...")
380
+ progress_bar.progress(75)
381
+ recommendations = agent.generate_recommendations(comparison, preferences)
382
+
383
+ # Step 4: Complete
384
+ status_text.text("βœ… Analysis complete!")
385
+ progress_bar.progress(100)
386
+ time.sleep(1)
387
+
388
+ # Clear progress
389
+ progress_container.empty()
390
+
391
+ if products and comparison and recommendations:
392
+ # Store results
393
+ st.session_state.search_results = {
394
+ 'query': product_query,
395
+ 'products': products,
396
+ 'comparison': comparison,
397
+ 'recommendations': recommendations,
398
+ 'preferences': preferences
399
+ }
400
+
401
+ st.success(f"βœ… Found {len(products)} products! Analysis complete.")
402
+ else:
403
+ st.warning("Please enter a product to search for.")
404
+
405
+ # Display results if available
406
+ if 'search_results' in st.session_state and st.session_state.search_results:
407
+ display_results(st.session_state.search_results)
408
+
409
+ def display_results(results):
410
+ """Display comprehensive search results"""
411
+
412
+ products = results['products']
413
+ comparison = results['comparison']
414
+ recommendations = results['recommendations']
415
+
416
+ # Top Recommendations Section
417
+ st.header("πŸ† Top Recommendations")
418
+
419
+ # Best Overall
420
+ if 'best_overall' in recommendations:
421
+ best = recommendations['best_overall']
422
+
423
+ st.subheader("πŸ₯‡ Best Overall Choice")
424
+ col1, col2, col3, col4 = st.columns(4)
425
+
426
+ with col1:
427
+ st.metric("Price", f"${best['price']}")
428
+ with col2:
429
+ st.metric("Rating", f"{best['rating']}/5")
430
+ with col3:
431
+ st.metric("Reviews", f"{best.get('reviews_count', 0):,}")
432
+ with col4:
433
+ st.metric("Score", f"{best['composite_score']:.2f}")
434
+
435
+ st.markdown(f"**{best['name']}**")
436
+ st.markdown(f"**Store:** {best['source']} | **Brand:** {best['specs'].get('brand', 'N/A')}")
437
+ st.markdown(f"**Shipping:** {best.get('shipping', 'Standard shipping')}")
438
+
439
+ # Category Winners
440
+ st.subheader("πŸŽ–οΈ Category Winners")
441
+
442
+ col1, col2, col3 = st.columns(3)
443
+
444
+ with col1:
445
+ st.markdown("**πŸ’° Best Value**")
446
+ if 'best_value' in recommendations:
447
+ best_value = recommendations['best_value']
448
+ st.markdown(f"{best_value['name']}")
449
+ st.markdown(f"${best_value['price']} β€’ {best_value['rating']}/5")
450
+ st.markdown(f"Store: {best_value['source']}")
451
+
452
+ with col2:
453
+ st.markdown("**⭐ Highest Rated**")
454
+ if 'highest_rated' in recommendations:
455
+ highest = recommendations['highest_rated']
456
+ st.markdown(f"{highest['name']}")
457
+ st.markdown(f"${highest['price']} β€’ {highest['rating']}/5")
458
+ st.markdown(f"Store: {highest['source']}")
459
+
460
+ with col3:
461
+ st.markdown("**πŸ‘₯ Most Reviewed**")
462
+ if 'most_reviewed' in recommendations:
463
+ most_rev = recommendations['most_reviewed']
464
+ st.markdown(f"{most_rev['name']}")
465
+ st.markdown(f"${most_rev['price']} β€’ {most_rev.get('reviews_count', 0):,} reviews")
466
+ st.markdown(f"Store: {most_rev['source']}")
467
+
468
+ # Market Analysis
469
+ st.header("πŸ“ˆ Market Analysis")
470
+
471
+ col1, col2, col3, col4 = st.columns(4)
472
+
473
+ with col1:
474
+ st.metric(
475
+ "Price Range",
476
+ f"${comparison['price_analysis']['min']:.0f} - ${comparison['price_analysis']['max']:.0f}"
477
+ )
478
+
479
+ with col2:
480
+ st.metric(
481
+ "Average Price",
482
+ f"${comparison['price_analysis']['average']:.0f}"
483
+ )
484
+
485
+ with col3:
486
+ st.metric(
487
+ "Average Rating",
488
+ f"{comparison['rating_analysis']['average']}/5"
489
+ )
490
+
491
+ with col4:
492
+ st.metric(
493
+ "Total Products",
494
+ f"{comparison['total_products']}"
495
+ )
496
+
497
+ # Detailed Comparison Table
498
+ st.header("πŸ“Š Product Comparison")
499
+
500
+ # Create comparison DataFrame
501
+ df_data = []
502
+ for product in products:
503
+ df_data.append({
504
+ 'Product': product['name'],
505
+ 'Price': f"${product['price']}",
506
+ 'Rating': f"{product['rating']}/5",
507
+ 'Reviews': f"{product.get('reviews_count', 0):,}",
508
+ 'Brand': product['specs'].get('brand', 'N/A'),
509
+ 'Store': product['source'],
510
+ 'Shipping': product.get('shipping', 'Standard')
511
+ })
512
+
513
+ df = pd.DataFrame(df_data)
514
+ st.dataframe(df, use_container_width=True, hide_index=True)
515
+
516
+ # Store and Brand Distribution
517
+ col1, col2 = st.columns(2)
518
+
519
+ with col1:
520
+ st.subheader("πŸͺ Store Distribution")
521
+ store_dist = comparison.get('store_distribution', {})
522
+ if store_dist:
523
+ st.bar_chart(store_dist)
524
+
525
+ with col2:
526
+ st.subheader("🏷️ Brand Distribution")
527
+ brand_dist = comparison.get('brand_distribution', {})
528
+ if brand_dist:
529
+ st.bar_chart(brand_dist)
530
+
531
+ # Detailed Product Cards
532
+ st.header("πŸ“‹ Detailed Product Information")
533
+
534
+ # Show top 5 products in expandable cards
535
+ top_products = recommendations.get('top_3', products[:3])
536
+
537
+ for i, product in enumerate(top_products):
538
+ with st.expander(f"πŸ” {product['name']} - ${product['price']}", expanded=(i==0)):
539
+ col1, col2 = st.columns([2, 1])
540
+
541
+ with col1:
542
+ st.markdown(f"**Price:** ${product['price']}")
543
+ st.markdown(f"**Rating:** {product['rating']}/5 ({product.get('reviews_count', 0):,} reviews)")
544
+ st.markdown(f"**Store:** {product['source']}")
545
+ st.markdown(f"**Shipping:** {product.get('shipping', 'Standard shipping')}")
546
+
547
+ # Specifications
548
+ st.markdown("**Specifications:**")
549
+ specs_text = []
550
+ for key, value in product['specs'].items():
551
+ if key != 'brand': # Brand already shown above
552
+ specs_text.append(f"β€’ **{key.replace('_', ' ').title()}:** {value}")
553
+ st.markdown('\n'.join(specs_text))
554
+
555
+ with col2:
556
+ if 'composite_score' in product:
557
+ st.markdown("**Scoring Breakdown:**")
558
+ st.markdown(f"Overall Score: {product['composite_score']:.3f}")
559
+ st.markdown(f"Price Score: {product.get('price_score', 0):.3f}")
560
+ st.markdown(f"Rating Score: {product.get('rating_score', 0):.3f}")
561
+ st.markdown(f"Reviews Score: {product.get('reviews_score', 0):.3f}")
562
+
563
+ if st.button(f"πŸ›’ View Product", key=f"view_{i}"):
564
+ st.markdown(f"[Open in {product['source']}]({product['url']})")
565
+
566
+ # Export Section
567
+ st.header("πŸ“₯ Export Results")
568
+
569
+ col1, col2 = st.columns(2)
570
+
571
+ with col1:
572
+ # JSON Export
573
+ if st.button("πŸ“„ Download JSON Report"):
574
+ report_data = {
575
+ 'search_query': results['query'],
576
+ 'search_timestamp': datetime.now().isoformat(),
577
+ 'preferences': results.get('preferences', ''),
578
+ 'summary': {
579
+ 'total_products': len(products),
580
+ 'price_range': f"${comparison['price_analysis']['min']:.0f} - ${comparison['price_analysis']['max']:.0f}",
581
+ 'average_rating': f"{comparison['rating_analysis']['average']:.1f}/5",
582
+ 'best_overall': recommendations.get('best_overall', {}).get('name', 'N/A')
583
+ },
584
+ 'products': products,
585
+ 'detailed_analysis': comparison,
586
+ 'recommendations': recommendations
587
+ }
588
+
589
+ st.download_button(
590
+ label="πŸ’Ύ Download Complete Report",
591
+ data=json.dumps(report_data, indent=2),
592
+ file_name=f"shopping_report_{results['query'].replace(' ', '_')}.json",
593
+ mime="application/json"
594
+ )
595
+
596
+ with col2:
597
+ # CSV Export
598
+ if st.button("πŸ“Š Download CSV"):
599
+ csv_data = pd.DataFrame(df_data)
600
+ st.download_button(
601
+ label="πŸ“ˆ Download Comparison CSV",
602
+ data=csv_data.to_csv(index=False),
603
+ file_name=f"product_comparison_{results['query'].replace(' ', '_')}.csv",
604
+ mime="text/csv"
605
+ )
606
+
607
+ # Add footer with instructions
608
+ def show_footer():
609
+ st.markdown("---")
610
+ st.markdown("""
611
+ <div style='text-align: center; color: #666;'>
612
+ <h4>πŸš€ Deploy on Hugging Face Spaces</h4>
613
+ <p>Save this as <code>app.py</code> and create a new Space with Streamlit SDK</p>
614
+ <p>No additional setup required - runs directly on HF Spaces!</p>
615
+ </div>
616
+ """, unsafe_allow_html=True)
617
+
618
+ if __name__ == "__main__":
619
+ main()
620
+ show_footer()
621
+
622
+ # Requirements.txt content for Hugging Face Spaces:
623
+ """
624
+ streamlit==1.28.0
625
+ pandas==2.0.3
626
+ requests==2.31.0
627
+ beautifulsoup4==4.12.2
628
+ """