SamiKoen commited on
Commit
56aafb6
·
verified ·
1 Parent(s): d5eef83

Upload 3 files

Browse files
Files changed (3) hide show
  1. app.py +58 -24
  2. enhanced_features.py +31 -3
  3. image_renderer.py +143 -0
app.py CHANGED
@@ -27,6 +27,7 @@ from enhanced_features import (
27
  initialize_enhanced_features, process_image_message,
28
  handle_comparison_request, get_user_recommendations, profile_manager
29
  )
 
30
 
31
  # Gradio uyarılarını bastır
32
  warnings.filterwarnings("ignore", category=UserWarning, module="gradio.components.chatbot")
@@ -171,14 +172,15 @@ if root is not None:
171
  except (ValueError, TypeError):
172
  price_rebate_money_order = price_rebate_money_order_str
173
 
174
- # Sadece ürün linkini al, resim linkini alma
175
  product_link = item.find('productLink').text if item.find('productLink') is not None else ""
 
176
 
177
- # Tüm fiyat bilgilerini birleştir
178
- item_info = (stock_amount, price, product_link, price_eft, price_rebate, price_rebate_money_order)
179
  else:
180
  # Stokta olmayan ürünler için sadece stok durumunu belirt, fiyat ve link bilgisi verme
181
- item_info = (stock_amount, "", "", "", "", "")
182
 
183
  products.append((name, item_info, full_name))
184
 
@@ -412,11 +414,14 @@ def chatbot_fn(user_message, history, image=None):
412
  if product_info[1][3] and product_info[1][3] != "":
413
  eft_price = f"\nHavale indirimli fiyat: {product_info[1][3]} TL"
414
 
415
- # Ürün linki
416
  product_link = f"\nÜrün linki: {product_info[1][2]}"
 
 
 
417
 
418
  # Tüm bilgileri birleştir
419
- new_msg = f"{product_info[2]} {product_info[1][0]}\n{normal_price}{rebate_price}{discount_info}{eft_price}{rebate_money_order_price}{product_link}"
420
  else:
421
  # Ürün stokta yoksa sadece stok durumunu bildir
422
  new_msg = f"{product_info[2]} {product_info[1][0]}"
@@ -456,11 +461,17 @@ def chatbot_fn(user_message, history, image=None):
456
  delta = chunk_data['choices'][0]['delta']
457
  if 'content' in delta:
458
  partial_response += delta['content']
459
- yield partial_response
 
 
460
  except json.JSONDecodeError as e:
461
  print(f"JSON parse hatası: {e} - Chunk: {chunk_str}")
462
  elif chunk_str == "data: [DONE]":
463
  break
 
 
 
 
464
 
465
  # Log: Asistan cevabını ekle
466
  try:
@@ -564,6 +575,35 @@ h3 {
564
  text-rendering: optimizeLegibility;
565
  }
566
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
567
  /* JavaScript ile placeholder eklemek için */
568
  </style>
569
  <script>
@@ -592,33 +632,27 @@ def enhanced_chatbot_fn(message, history, image):
592
  # Demo arayüzü - Özel layout ile image upload destekli
593
  with gr.Blocks(css=custom_css, theme="soft", title="Trek Asistanı") as demo:
594
  gr.Markdown("# 🚲 Trek Asistanı AI")
595
- gr.Markdown("**Yeni özellikler:** Bisiklet fotoğrafı yükleyebilir, ürün karşılaştırması yapabilir ve kişisel öneriler alabilirsiniz!")
596
 
597
  with gr.Row():
598
  with gr.Column(scale=4):
599
- chatbot = gr.Chatbot(height=500)
600
 
601
  with gr.Column(scale=1):
602
- gr.Markdown("### 🖼️ Bisiklet Fotoğrafı")
603
- image_input = gr.Image(type="filepath", height=200)
604
- gr.Markdown("### 🔍 Özel Komutlar")
605
- gr.Markdown("""
606
- • **Karşılaştırma:** "Émonda ve Madone karşılaştır"
607
- • **Bütçe:** "50-100K TL bütçem var"
608
- • **Görsel:** Bisiklet fotoğrafı yükle
609
- """)
610
 
611
  with gr.Row():
612
  msg = gr.Textbox(
613
- placeholder="Sorunuzu yazın veya fotoğraf yükleyip analiz isteyin...",
614
  show_label=False,
615
  scale=4
616
  )
617
  submit_btn = gr.Button("Gönder", scale=1)
618
 
619
- def respond(message, chat_history, image):
620
- # Enhanced chatbot fonksiyonunu çağır
621
- response_generator = chatbot_fn(message, chat_history, image)
622
 
623
  # Generator'dan son cevabı al
624
  response = ""
@@ -627,10 +661,10 @@ with gr.Blocks(css=custom_css, theme="soft", title="Trek Asistanı") as demo:
627
 
628
  # Chat history güncelle
629
  chat_history.append((message, response))
630
- return "", chat_history, None
631
 
632
- submit_btn.click(respond, [msg, chatbot, image_input], [msg, chatbot, image_input])
633
- msg.submit(respond, [msg, chatbot, image_input], [msg, chatbot, image_input])
634
 
635
  if __name__ == "__main__":
636
  demo.launch(debug=True)
 
27
  initialize_enhanced_features, process_image_message,
28
  handle_comparison_request, get_user_recommendations, profile_manager
29
  )
30
+ from image_renderer import extract_product_info_for_gallery, format_message_with_images
31
 
32
  # Gradio uyarılarını bastır
33
  warnings.filterwarnings("ignore", category=UserWarning, module="gradio.components.chatbot")
 
172
  except (ValueError, TypeError):
173
  price_rebate_money_order = price_rebate_money_order_str
174
 
175
+ # Ürün linkini ve resim URL'sini al
176
  product_link = item.find('productLink').text if item.find('productLink') is not None else ""
177
+ image_url = item.find('picture1Path').text if item.find('picture1Path') is not None else ""
178
 
179
+ # Tüm fiyat bilgilerini birleştir (resim URL'si eklendi)
180
+ item_info = (stock_amount, price, product_link, price_eft, price_rebate, price_rebate_money_order, image_url)
181
  else:
182
  # Stokta olmayan ürünler için sadece stok durumunu belirt, fiyat ve link bilgisi verme
183
+ item_info = (stock_amount, "", "", "", "", "", "")
184
 
185
  products.append((name, item_info, full_name))
186
 
 
414
  if product_info[1][3] and product_info[1][3] != "":
415
  eft_price = f"\nHavale indirimli fiyat: {product_info[1][3]} TL"
416
 
417
+ # Ürün linki ve resim
418
  product_link = f"\nÜrün linki: {product_info[1][2]}"
419
+ product_image = ""
420
+ if len(product_info[1]) > 6 and product_info[1][6]: # Resim URL'si varsa
421
+ product_image = f"\nÜrün resmi: {product_info[1][6]}"
422
 
423
  # Tüm bilgileri birleştir
424
+ new_msg = f"{product_info[2]} {product_info[1][0]}\n{normal_price}{rebate_price}{discount_info}{eft_price}{rebate_money_order_price}{product_link}{product_image}"
425
  else:
426
  # Ürün stokta yoksa sadece stok durumunu bildir
427
  new_msg = f"{product_info[2]} {product_info[1][0]}"
 
461
  delta = chunk_data['choices'][0]['delta']
462
  if 'content' in delta:
463
  partial_response += delta['content']
464
+ # Resim formatlaması uygula
465
+ formatted_response = extract_product_info_for_gallery(partial_response)
466
+ yield formatted_response
467
  except json.JSONDecodeError as e:
468
  print(f"JSON parse hatası: {e} - Chunk: {chunk_str}")
469
  elif chunk_str == "data: [DONE]":
470
  break
471
+
472
+ # Son resim formatlaması
473
+ final_response = extract_product_info_for_gallery(partial_response)
474
+ yield final_response
475
 
476
  # Log: Asistan cevabını ekle
477
  try:
 
575
  text-rendering: optimizeLegibility;
576
  }
577
 
578
+ /* Resim gösterimi için özel stiller */
579
+ #chatbot .message img {
580
+ max-width: 100%;
581
+ height: auto;
582
+ border-radius: 8px;
583
+ margin: 8px 0;
584
+ }
585
+
586
+ #chatbot .message {
587
+ word-wrap: break-word;
588
+ }
589
+
590
+ /* Ürün galeri stilleri */
591
+ .product-gallery {
592
+ display: flex;
593
+ flex-wrap: wrap;
594
+ gap: 10px;
595
+ margin: 10px 0;
596
+ }
597
+
598
+ .product-card {
599
+ border: 1px solid #ddd;
600
+ border-radius: 8px;
601
+ padding: 8px;
602
+ max-width: 180px;
603
+ text-align: center;
604
+ background: white;
605
+ }
606
+
607
  /* JavaScript ile placeholder eklemek için */
608
  </style>
609
  <script>
 
632
  # Demo arayüzü - Özel layout ile image upload destekli
633
  with gr.Blocks(css=custom_css, theme="soft", title="Trek Asistanı") as demo:
634
  gr.Markdown("# 🚲 Trek Asistanı AI")
635
+ gr.Markdown("**Akıllı özellikler:** Ürün karşılaştırması, kişisel öneriler ve detaylı ürün bilgileri sunuyorum.")
636
 
637
  with gr.Row():
638
  with gr.Column(scale=4):
639
+ chatbot = gr.Chatbot(height=500, elem_id="chatbot", show_label=False)
640
 
641
  with gr.Column(scale=1):
642
+ gr.Markdown("### ℹ️ Bilgi")
643
+ gr.Markdown("Trek bisiklet uzmanı AI asistanınız. Ürün bilgileri, fiyatlar ve öneriler için sorularınızı yazın.")
 
 
 
 
 
 
644
 
645
  with gr.Row():
646
  msg = gr.Textbox(
647
+ placeholder="Trek bisikletleri hakkında soru sorun...",
648
  show_label=False,
649
  scale=4
650
  )
651
  submit_btn = gr.Button("Gönder", scale=1)
652
 
653
+ def respond(message, chat_history):
654
+ # Enhanced chatbot fonksiyonunu çağır (image=None)
655
+ response_generator = chatbot_fn(message, chat_history, None)
656
 
657
  # Generator'dan son cevabı al
658
  response = ""
 
661
 
662
  # Chat history güncelle
663
  chat_history.append((message, response))
664
+ return "", chat_history
665
 
666
+ submit_btn.click(respond, [msg, chatbot], [msg, chatbot])
667
+ msg.submit(respond, [msg, chatbot], [msg, chatbot])
668
 
669
  if __name__ == "__main__":
670
  demo.launch(debug=True)
enhanced_features.py CHANGED
@@ -181,6 +181,25 @@ class ProductComparison:
181
  def __init__(self, products_data):
182
  self.products = products_data
183
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
184
  def find_products_by_name(self, product_names):
185
  """İsimlere göre ürünleri bul"""
186
  found_products = []
@@ -205,14 +224,23 @@ class ProductComparison:
205
 
206
  # Ürün bilgilerini parse et
207
  stock_status = item_info[0] if len(item_info) > 0 else "Bilgi yok"
208
- price = item_info[1] if len(item_info) > 1 and item_info[1] else "Fiyat yok"
209
  product_link = item_info[2] if len(item_info) > 2 else ""
 
 
 
 
 
 
 
 
210
 
211
  comparison_data.append({
212
  "Ürün": full_name,
213
  "Stok": stock_status,
214
- "Fiyat": f"{price} TL" if price != "Fiyat yok" else price,
215
- "Link": product_link
 
216
  })
217
 
218
  # DataFrame oluştur
 
181
  def __init__(self, products_data):
182
  self.products = products_data
183
 
184
+ def round_price(self, price_str):
185
+ """Fiyatı yuvarlama formülüne göre yuvarla"""
186
+ try:
187
+ price_float = float(price_str)
188
+ # Fiyat 200000 üzerindeyse en yakın 5000'lik basamağa yuvarla
189
+ if price_float > 200000:
190
+ return str(round(price_float / 5000) * 5000)
191
+ # Fiyat 30000 üzerindeyse en yakın 1000'lik basamağa yuvarla
192
+ elif price_float > 30000:
193
+ return str(round(price_float / 1000) * 1000)
194
+ # Fiyat 10000 üzerindeyse en yakın 100'lük basamağa yuvarla
195
+ elif price_float > 10000:
196
+ return str(round(price_float / 100) * 100)
197
+ # Diğer durumlarda en yakın 10'luk basamağa yuvarla
198
+ else:
199
+ return str(round(price_float / 10) * 10)
200
+ except (ValueError, TypeError):
201
+ return price_str
202
+
203
  def find_products_by_name(self, product_names):
204
  """İsimlere göre ürünleri bul"""
205
  found_products = []
 
224
 
225
  # Ürün bilgilerini parse et
226
  stock_status = item_info[0] if len(item_info) > 0 else "Bilgi yok"
227
+ price_raw = item_info[1] if len(item_info) > 1 and item_info[1] else "Fiyat yok"
228
  product_link = item_info[2] if len(item_info) > 2 else ""
229
+ image_url = item_info[6] if len(item_info) > 6 and item_info[6] else ""
230
+
231
+ # Fiyatı yuvarlama formülüne göre yuvarla
232
+ if price_raw != "Fiyat yok":
233
+ price = self.round_price(price_raw)
234
+ price_display = f"{price} TL"
235
+ else:
236
+ price_display = price_raw
237
 
238
  comparison_data.append({
239
  "Ürün": full_name,
240
  "Stok": stock_status,
241
+ "Fiyat": price_display,
242
+ "Link": product_link,
243
+ "Resim": image_url if image_url else "Resim yok"
244
  })
245
 
246
  # DataFrame oluştur
image_renderer.py ADDED
@@ -0,0 +1,143 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Ürün Resimlerini Sohbette Gösterme Sistemi
4
+ """
5
+
6
+ def round_price(price_str):
7
+ """Fiyatı yuvarlama formülüne göre yuvarla"""
8
+ try:
9
+ # TL ve diğer karakterleri temizle
10
+ price_clean = price_str.replace(' TL', '').replace(',', '.')
11
+ price_float = float(price_clean)
12
+
13
+ # Fiyat 200000 üzerindeyse en yakın 5000'lik basamağa yuvarla
14
+ if price_float > 200000:
15
+ return str(round(price_float / 5000) * 5000)
16
+ # Fiyat 30000 üzerindeyse en yakın 1000'lik basamağa yuvarla
17
+ elif price_float > 30000:
18
+ return str(round(price_float / 1000) * 1000)
19
+ # Fiyat 10000 üzerindeyse en yakın 100'lük basamağa yuvarla
20
+ elif price_float > 10000:
21
+ return str(round(price_float / 100) * 100)
22
+ # Diğer durumlarda en yakın 10'luk basamağa yuvarla
23
+ else:
24
+ return str(round(price_float / 10) * 10)
25
+ except (ValueError, TypeError):
26
+ return price_str
27
+
28
+ def format_message_with_images(message):
29
+ """Mesajdaki resim URL'lerini HTML formatına çevir"""
30
+ if "Ürün resmi:" not in message:
31
+ return message
32
+
33
+ lines = message.split('\n')
34
+ formatted_lines = []
35
+
36
+ for line in lines:
37
+ if line.startswith("Ürün resmi:"):
38
+ image_url = line.replace("Ürün resmi:", "").strip()
39
+ if image_url:
40
+ # HTML img tag'i oluştur
41
+ img_html = f"""
42
+ <div style="margin: 10px 0;">
43
+ <img src="{image_url}"
44
+ alt="Ürün Resmi"
45
+ style="max-width: 300px; max-height: 200px; border-radius: 8px; border: 1px solid #ddd; box-shadow: 0 2px 4px rgba(0,0,0,0.1);"
46
+ onerror="this.style.display='none'">
47
+ </div>"""
48
+ formatted_lines.append(img_html)
49
+ else:
50
+ formatted_lines.append(line)
51
+ else:
52
+ formatted_lines.append(line)
53
+
54
+ return '\n'.join(formatted_lines)
55
+
56
+ def create_product_gallery(products_with_images):
57
+ """Birden fazla ürün için galeri oluştur"""
58
+ if not products_with_images:
59
+ return ""
60
+
61
+ gallery_html = """
62
+ <div style="display: flex; flex-wrap: wrap; gap: 15px; margin: 15px 0;">
63
+ """
64
+
65
+ for product in products_with_images:
66
+ name = product.get('name', 'Bilinmeyen')
67
+ price = product.get('price', 'Fiyat yok')
68
+ image_url = product.get('image_url', '')
69
+ product_url = product.get('product_url', '')
70
+
71
+ if image_url:
72
+ card_html = f"""
73
+ <div style="border: 1px solid #ddd; border-radius: 8px; padding: 10px; max-width: 200px; text-align: center; background: white; box-shadow: 0 2px 4px rgba(0,0,0,0.1);">
74
+ <img src="{image_url}"
75
+ alt="{name}"
76
+ style="max-width: 180px; max-height: 120px; border-radius: 4px; margin-bottom: 8px;"
77
+ onerror="this.style.display='none'">
78
+ <div style="font-size: 12px; font-weight: bold; margin-bottom: 4px;">{name}</div>
79
+ <div style="font-size: 11px; color: #666; margin-bottom: 8px;">{price}</div>
80
+ {f'<a href="{product_url}" target="_blank" style="font-size: 10px; color: #007bff; text-decoration: none;">Detaylı Bilgi</a>' if product_url else ''}
81
+ </div>"""
82
+ gallery_html += card_html
83
+
84
+ gallery_html += "</div>"
85
+ return gallery_html
86
+
87
+ def extract_product_info_for_gallery(message):
88
+ """Mesajdan ürün bilgilerini çıkarıp galeri formatına çevir"""
89
+ if "karşılaştır" in message.lower() or "öneri" in message.lower():
90
+ # Bu durumda galeri formatı kullan
91
+ lines = message.split('\n')
92
+ products = []
93
+
94
+ current_product = {}
95
+ for line in lines:
96
+ line = line.strip()
97
+ if line.startswith('•') and any(keyword in line.lower() for keyword in ['marlin', 'émonda', 'madone', 'domane', 'fuel', 'powerfly', 'fx']):
98
+ # Yeni ürün başladı
99
+ if current_product:
100
+ products.append(current_product)
101
+
102
+ # Ürün adı ve fiyatı parse et
103
+ parts = line.split(' - ')
104
+ name = parts[0].replace('•', '').strip()
105
+ price_raw = parts[1] if len(parts) > 1 else 'Fiyat yok'
106
+
107
+ # Fiyatı yuvarlama formülüne göre yuvarla
108
+ if price_raw != 'Fiyat yok':
109
+ price = round_price(price_raw) + ' TL'
110
+ else:
111
+ price = price_raw
112
+
113
+ current_product = {
114
+ 'name': name,
115
+ 'price': price,
116
+ 'image_url': '',
117
+ 'product_url': ''
118
+ }
119
+ elif "Ürün resmi:" in line and current_product:
120
+ current_product['image_url'] = line.replace("Ürün resmi:", "").strip()
121
+ elif "Ürün linki:" in line and current_product:
122
+ current_product['product_url'] = line.replace("Ürün linki:", "").strip()
123
+
124
+ # Son ürünü ekle
125
+ if current_product:
126
+ products.append(current_product)
127
+
128
+ if products:
129
+ gallery = create_product_gallery(products)
130
+ # Orijinal mesajdaki resim linklerini temizle
131
+ cleaned_message = message
132
+ for line in message.split('\n'):
133
+ if line.startswith("Ürün resmi:") or line.startswith("Ürün linki:"):
134
+ cleaned_message = cleaned_message.replace(line, "")
135
+
136
+ return cleaned_message.strip() + "\n\n" + gallery
137
+
138
+ return format_message_with_images(message)
139
+
140
+ def should_use_gallery_format(message):
141
+ """Mesajın galeri formatı kullanması gerekip gerekmediğini kontrol et"""
142
+ gallery_keywords = ["karşılaştır", "öneri", "seçenek", "alternatif", "bütçe"]
143
+ return any(keyword in message.lower() for keyword in gallery_keywords)