SamiKoen commited on
Commit
e2a874a
·
verified ·
1 Parent(s): 73cc2c5

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +111 -605
app.py CHANGED
@@ -6,7 +6,6 @@ import xml.etree.ElementTree as ET
6
  import schedule
7
  import time
8
  import threading
9
- from concurrent.futures import ThreadPoolExecutor, as_completed
10
  from huggingface_hub import HfApi, create_repo, hf_hub_download
11
  import warnings
12
  import pandas as pd
@@ -30,415 +29,13 @@ from enhanced_features import (
30
  )
31
  from image_renderer import extract_product_info_for_gallery, format_message_with_images
32
 
33
- # STOK API ENTEGRASYONU - YENİ EKLENEN KISIM
34
- STOCK_API_BASE = "https://video.trek-turkey.com/bizimhesap-proxy.php"
35
-
36
- # Stock cache (5 dakikalık cache)
37
- stock_cache = {}
38
- CACHE_DURATION = 300 # 5 dakika (saniye cinsinden)
39
-
40
- def normalize_turkish(text):
41
- """Türkçe karakterleri normalize et"""
42
- if not text:
43
- return ""
44
- replacements = {
45
- 'ı': 'i', 'İ': 'i', 'ş': 's', 'Ş': 's',
46
- 'ğ': 'g', 'Ğ': 'g', 'ü': 'u', 'Ü': 'u',
47
- 'ö': 'o', 'Ö': 'o', 'ç': 'c', 'Ç': 'c'
48
- }
49
- text = text.lower()
50
- for tr_char, eng_char in replacements.items():
51
- text = text.replace(tr_char, eng_char)
52
- return text
53
-
54
- def fetch_warehouse_inventory(warehouse, product_name, search_terms):
55
- """Tek bir mağazanın stok bilgisini al"""
56
- try:
57
- warehouse_id = warehouse['id']
58
- warehouse_name = warehouse['title']
59
-
60
- # DSW'yi ayrı tut (gelecek stok için)
61
- is_dsw = 'DSW' in warehouse_name or 'ÖN SİPARİŞ' in warehouse_name.upper()
62
-
63
- # Mağaza stoklarını al
64
- inventory_url = f"{STOCK_API_BASE}?action=inventory&warehouse={warehouse_id}&endpoint=inventory/{warehouse_id}"
65
- inventory_response = requests.get(inventory_url, timeout=3, verify=False)
66
-
67
- if inventory_response.status_code != 200:
68
- return None
69
-
70
- inventory_data = inventory_response.json()
71
-
72
- # API yanıtını kontrol et
73
- if 'data' not in inventory_data or 'inventory' not in inventory_data['data']:
74
- return None
75
-
76
- products = inventory_data['data']['inventory']
77
-
78
- # Beden terimleri kontrolü
79
- size_terms = ['xs', 's', 'm', 'ml', 'l', 'xl', 'xxl', '2xl', '3xl', 'small', 'medium', 'large']
80
- size_numbers = ['44', '46', '48', '50', '52', '54', '56', '58', '60']
81
-
82
- # Arama terimlerinde beden var mı kontrol et
83
- has_size_query = False
84
- size_query = None
85
- for term in search_terms:
86
- if term in size_terms or term in size_numbers:
87
- has_size_query = True
88
- size_query = term
89
- break
90
-
91
- # Eğer sadece beden sorgusu varsa (ör: "m", "xl")
92
- is_only_size_query = len(search_terms) == 1 and has_size_query
93
-
94
- # Ürünü ara
95
- warehouse_variants = []
96
- dsw_stock_count = 0
97
-
98
- for product in products:
99
- product_title = normalize_turkish(product.get('title', '')).lower()
100
- original_title = product.get('title', '')
101
-
102
- # Eğer sadece beden sorgusu ise
103
- if is_only_size_query:
104
- # Beden terimini ürün başlığında ara (parantez içinde veya dışında)
105
- # Örnek: "Marlin 5 (M)" veya "Marlin 5 M" veya "Marlin 5 - M"
106
- if size_query in product_title.split() or f'({size_query})' in product_title or f' {size_query} ' in product_title or product_title.endswith(f' {size_query}'):
107
- qty = int(product.get('qty', 0))
108
- stock = int(product.get('stock', 0))
109
- actual_stock = max(qty, stock)
110
-
111
- if actual_stock > 0:
112
- if is_dsw:
113
- dsw_stock_count += actual_stock
114
- continue
115
- warehouse_variants.append(f"{original_title}: ✓ Stokta")
116
- else:
117
- # Normal ürün araması - tüm terimler eşleşmeli
118
- # Ama beden terimi varsa özel kontrol yap
119
- if has_size_query:
120
- # Beden hariç diğer terimleri kontrol et
121
- non_size_terms = [t for t in search_terms if t != size_query]
122
- product_matches = all(term in product_title for term in non_size_terms)
123
-
124
- # Beden kontrolü - daha esnek
125
- size_matches = size_query in product_title.split() or f'({size_query})' in product_title or f' {size_query} ' in product_title or product_title.endswith(f' {size_query}')
126
-
127
- if product_matches and size_matches:
128
- qty = int(product.get('qty', 0))
129
- stock = int(product.get('stock', 0))
130
- actual_stock = max(qty, stock)
131
-
132
- if actual_stock > 0:
133
- if is_dsw:
134
- dsw_stock_count += actual_stock
135
- continue
136
-
137
- # Varyant bilgisini göster
138
- variant_info = original_title
139
- possible_names = [
140
- product_name.upper(),
141
- product_name.lower(),
142
- product_name.title(),
143
- product_name.upper().replace('I', 'İ'),
144
- product_name.upper().replace('İ', 'I'),
145
- ]
146
-
147
- if 'fx sport' in product_name.lower():
148
- possible_names.extend(['FX Sport AL 3', 'FX SPORT AL 3', 'Fx Sport Al 3'])
149
-
150
- for possible_name in possible_names:
151
- variant_info = variant_info.replace(possible_name, '').strip()
152
-
153
- variant_info = ' '.join(variant_info.split())
154
-
155
- if variant_info and variant_info != original_title:
156
- warehouse_variants.append(f"{variant_info}: ✓ Stokta")
157
- else:
158
- warehouse_variants.append(f"{original_title}: ✓ Stokta")
159
- else:
160
- # Beden sorgusu yoksa normal kontrol
161
- if all(term in product_title for term in search_terms):
162
- qty = int(product.get('qty', 0))
163
- stock = int(product.get('stock', 0))
164
- actual_stock = max(qty, stock)
165
-
166
- if actual_stock > 0:
167
- if is_dsw:
168
- dsw_stock_count += actual_stock
169
- continue
170
-
171
- variant_info = original_title
172
- possible_names = [
173
- product_name.upper(),
174
- product_name.lower(),
175
- product_name.title(),
176
- product_name.upper().replace('I', 'İ'),
177
- product_name.upper().replace('İ', 'I'),
178
- ]
179
-
180
- if 'fx sport' in product_name.lower():
181
- possible_names.extend(['FX Sport AL 3', 'FX SPORT AL 3', 'Fx Sport Al 3'])
182
-
183
- for possible_name in possible_names:
184
- variant_info = variant_info.replace(possible_name, '').strip()
185
-
186
- variant_info = ' '.join(variant_info.split())
187
-
188
- if variant_info and variant_info != original_title:
189
- warehouse_variants.append(f"{variant_info}: ✓ Stokta")
190
- else:
191
- warehouse_variants.append(f"{original_title}: ✓ Stokta")
192
-
193
- # Sonuç döndür
194
- if warehouse_variants and not is_dsw:
195
- return {'warehouse': warehouse_name, 'variants': warehouse_variants, 'is_dsw': False}
196
- elif dsw_stock_count > 0:
197
- return {'dsw_stock': dsw_stock_count, 'is_dsw': True}
198
-
199
- return None
200
-
201
- except Exception:
202
- return None
203
-
204
- def get_realtime_stock_parallel(product_name):
205
- """API'den gerçek zamanlı stok bilgisini çek - Paralel versiyon with cache"""
206
- try:
207
- # Cache kontrolü
208
- cache_key = normalize_turkish(product_name).lower()
209
- current_time = time.time()
210
-
211
- if cache_key in stock_cache:
212
- cached_data, cached_time = stock_cache[cache_key]
213
- # Cache hala geçerli mi?
214
- if current_time - cached_time < CACHE_DURATION:
215
- print(f"Cache'den döndürülüyor: {product_name}")
216
- return cached_data
217
-
218
- # Önce mağaza listesini al
219
- warehouses_url = f"{STOCK_API_BASE}?action=warehouses&endpoint=warehouses"
220
- warehouses_response = requests.get(warehouses_url, timeout=3, verify=False)
221
-
222
- if warehouses_response.status_code != 200:
223
- print(f"Mağaza listesi alınamadı: {warehouses_response.status_code}")
224
- return None
225
-
226
- warehouses_data = warehouses_response.json()
227
-
228
- # API yanıtını kontrol et
229
- if 'data' not in warehouses_data or 'warehouses' not in warehouses_data['data']:
230
- print("Mağaza verisi bulunamadı")
231
- return None
232
-
233
- warehouses = warehouses_data['data']['warehouses']
234
-
235
- # Ürün adını normalize et
236
- search_terms = normalize_turkish(product_name).lower().split()
237
- print(f"Aranan ürün: {product_name} -> {search_terms}")
238
-
239
- stock_info = {}
240
- total_dsw_stock = 0
241
- total_stock = 0
242
-
243
- # Paralel olarak tüm mağazaları sorgula
244
- with ThreadPoolExecutor(max_workers=10) as executor:
245
- # Tüm mağazalar için görev oluştur
246
- futures = {
247
- executor.submit(fetch_warehouse_inventory, warehouse, product_name, search_terms): warehouse
248
- for warehouse in warehouses
249
- }
250
-
251
- # Sonuçları topla
252
- for future in as_completed(futures):
253
- result = future.result()
254
- if result:
255
- if result.get('is_dsw'):
256
- total_dsw_stock += result.get('dsw_stock', 0)
257
- else:
258
- warehouse_name = result['warehouse']
259
- stock_info[warehouse_name] = result['variants']
260
- total_stock += 1 # En az bir mağazada var
261
-
262
- # Sonucu oluştur
263
- if not stock_info:
264
- # Mağazada yok ama DSW'de varsa
265
- if total_dsw_stock > 0:
266
- result = f"{product_name}: Şu anda mağazalarda stokta yok, ancak yakında gelecek. Ön sipariş verebilirsiniz."
267
- else:
268
- result = f"{product_name}: Şu anda hiçbir mağazada stokta bulunmuyor."
269
- else:
270
- # Minimal prompt oluştur - varyant detaylarıyla
271
- prompt_lines = [f"{product_name} stok durumu:"]
272
- for warehouse, variants in stock_info.items():
273
- if isinstance(variants, list):
274
- prompt_lines.append(f"- {warehouse}:")
275
- for variant in variants:
276
- prompt_lines.append(f" • {variant}")
277
- else:
278
- prompt_lines.append(f"- {warehouse}: {variants}")
279
-
280
- # Güvenlik: Toplam adet bilgisi gösterme
281
- if total_stock > 0:
282
- prompt_lines.append(f"✓ Ürün stokta mevcut")
283
-
284
- result = "\n".join(prompt_lines)
285
-
286
- # Sonucu cache'e kaydet
287
- stock_cache[cache_key] = (result, current_time)
288
-
289
- return result
290
-
291
- except Exception as e:
292
- print(f"API hatası: {e}")
293
- return None
294
-
295
- def get_realtime_stock(product_name):
296
- """API'den gerçek zamanlı stok bilgisini çek - Minimal versiyon"""
297
- try:
298
- # Önce mağaza listesini al - endpoint parametresi eklendi
299
- warehouses_url = f"{STOCK_API_BASE}?action=warehouses&endpoint=warehouses"
300
- warehouses_response = requests.get(warehouses_url, timeout=3, verify=False) # Timeout 3 saniye
301
-
302
- if warehouses_response.status_code != 200:
303
- print(f"Mağaza listesi alınamadı: {warehouses_response.status_code}")
304
- return None
305
-
306
- warehouses_data = warehouses_response.json()
307
-
308
- # API yanıtını kontrol et
309
- if 'data' in warehouses_data and 'warehouses' in warehouses_data['data']:
310
- warehouses = warehouses_data['data']['warehouses']
311
- else:
312
- print("Mağaza verisi bulunamadı")
313
- return None
314
-
315
- # Ürün adını normalize et
316
- search_terms = normalize_turkish(product_name).lower().split()
317
- print(f"Aranan ürün: {product_name} -> {search_terms}")
318
-
319
- # Her mağazanın stok bilgisini topla
320
- stock_info = {}
321
- total_stock = 0
322
- dsw_stock = 0 # DSW deposundaki gelecek stok
323
-
324
- for warehouse in warehouses:
325
- warehouse_id = warehouse['id']
326
- warehouse_name = warehouse['title']
327
-
328
- # DSW'yi ayrı tut (gelecek stok için)
329
- is_dsw = 'DSW' in warehouse_name or 'ÖN SİPARİŞ' in warehouse_name.upper()
330
-
331
- # Mağaza stoklarını al - endpoint parametresi eklendi
332
- inventory_url = f"{STOCK_API_BASE}?action=inventory&warehouse={warehouse_id}&endpoint=inventory/{warehouse_id}"
333
- inventory_response = requests.get(inventory_url, timeout=3, verify=False) # Timeout 3 saniye
334
-
335
- if inventory_response.status_code != 200:
336
- continue
337
-
338
- inventory_data = inventory_response.json()
339
-
340
- # API yanıtını kontrol et
341
- if 'data' in inventory_data and 'inventory' in inventory_data['data']:
342
- products = inventory_data['data']['inventory']
343
- else:
344
- continue
345
-
346
- # Ürünü ara - TÜM VARYANTLARI TOPLA VE DETAYLARI KAYDET
347
- warehouse_variants = []
348
- for product in products:
349
- product_title = normalize_turkish(product.get('title', '')).lower()
350
- original_title = product.get('title', '')
351
-
352
- # Tüm arama terimlerinin ürün başlığında olup olmadığını kontrol et
353
- if all(term in product_title for term in search_terms):
354
- # qty ve stock değerlerini kontrol et
355
- qty = int(product.get('qty', 0))
356
- stock = int(product.get('stock', 0))
357
- actual_stock = max(qty, stock) # İkisinden büyük olanı al
358
-
359
- if actual_stock > 0:
360
- # DSW ise gelecek stok olarak say
361
- if is_dsw:
362
- dsw_stock += actual_stock
363
- continue # DSW'yi mağaza listesine ekleme
364
-
365
- # Normal mağaza stoğu
366
- # Varyant detayını sakla (beden/renk bilgisi için)
367
- # Ürün adını temizle - Türkçe karakterleri de dikkate al
368
- variant_info = original_title
369
-
370
- # Hem normal hem de Türkçe karakterli versiyonları dene
371
- possible_names = [
372
- product_name.upper(),
373
- product_name.lower(),
374
- product_name.title(),
375
- product_name.upper().replace('I', 'İ'), # Türkçe İ
376
- product_name.upper().replace('İ', 'I'), # İngilizce I
377
- ]
378
-
379
- # FX Sport AL 3 gibi özel durumlar için
380
- if 'fx sport' in product_name.lower():
381
- possible_names.extend(['FX Sport AL 3', 'FX SPORT AL 3', 'Fx Sport Al 3'])
382
-
383
- for possible_name in possible_names:
384
- variant_info = variant_info.replace(possible_name, '').strip()
385
-
386
- # Gereksiz boşlukları temizle
387
- variant_info = ' '.join(variant_info.split())
388
-
389
- if variant_info and variant_info != original_title:
390
- # Güvenlik: Adet yerine sadece stokta bilgisi
391
- warehouse_variants.append(f"{variant_info}: ✓ Stokta")
392
- else:
393
- # Eğer temizleme başarısız olduysa tam ismi göster
394
- warehouse_variants.append(f"{original_title}: ✓ Stokta")
395
- total_stock += actual_stock
396
-
397
- # DSW değilse mağaza listesine ekle
398
- if warehouse_variants and not is_dsw:
399
- stock_info[warehouse_name] = warehouse_variants
400
-
401
- if not stock_info:
402
- # Mağazada yok ama DSW'de varsa
403
- if dsw_stock > 0:
404
- return f"{product_name}: Şu anda mağazalarda stokta yok, ancak yakında gelecek. Ön sipariş verebilirsiniz."
405
- else:
406
- return f"{product_name}: Şu anda hiçbir mağazada stokta bulunmuyor."
407
-
408
- # Minimal prompt oluştur - varyant detaylarıyla
409
- prompt_lines = [f"{product_name} stok durumu:"]
410
- for warehouse, variants in stock_info.items():
411
- if isinstance(variants, list):
412
- # Varyant detayları varsa
413
- prompt_lines.append(f"- {warehouse}:")
414
- for variant in variants:
415
- prompt_lines.append(f" • {variant}")
416
- else:
417
- # Eski format (eğer hata olursa)
418
- prompt_lines.append(f"- {warehouse}: {variants} adet")
419
- # Güvenlik: Toplam adet bilgisi gösterme
420
- if total_stock > 0:
421
- prompt_lines.append(f"✓ Ürün stokta mevcut")
422
- # Mağazada varsa DSW bilgisi verme
423
- else:
424
- prompt_lines.append(f"✗ Stokta yok")
425
-
426
- return "\n".join(prompt_lines)
427
-
428
- except Exception as e:
429
- print(f"Stok API hatası: {e}")
430
- return None
431
-
432
- def is_stock_query(message):
433
- """Mesajın stok sorgusu olup olmadığını kontrol et"""
434
- stock_keywords = [
435
- 'stok', 'stock', 'kaç adet', 'kaç tane', 'var mı',
436
- 'mevcut mu', 'bulunuyor mu', 'hangi mağaza', 'nerede var',
437
- 'stok durumu', 'stoklarda', 'stokta', 'adet', 'kaç tane var'
438
- ]
439
- message_lower = message.lower()
440
- return any(keyword in message_lower for keyword in stock_keywords)
441
- # STOK API ENTEGRASYONU SONU
442
 
443
  # Gradio uyarılarını bastır
444
  warnings.filterwarnings("ignore", category=UserWarning, module="gradio.components.chatbot")
@@ -455,7 +52,7 @@ if not OPENAI_API_KEY:
455
 
456
  # Trek bisiklet ürünlerini çekme
457
  url = 'https://www.trekbisiklet.com.tr/output/8582384479'
458
- response = requests.get(url, verify=False, timeout=60)
459
  if response.status_code == 200 and response.content:
460
  root = ET.fromstring(response.content)
461
  else:
@@ -590,21 +187,8 @@ if root is not None:
590
  product_info.append("") # Boş resim URL'si
591
 
592
  else:
593
- # Stokta olmayan ürün için de fiyat bilgilerini sakla (API için gerekli)
594
- # Normal fiyat bilgisini al
595
- price_elem = item.find('priceTaxWithCur')
596
- price_str = price_elem.text if price_elem is not None and price_elem.text else "Fiyat bilgisi yok"
597
-
598
- # Ürün URL'si
599
- product_url_elem = item.find('productLink')
600
- product_url = product_url_elem.text if product_url_elem is not None and product_url_elem.text else ""
601
-
602
- # Resim URL'si
603
- image_elem = item.find('picture1Path')
604
- image_url = image_elem.text if image_elem is not None and image_elem.text else ""
605
-
606
- # Stokta olmayan ürün için de tüm bilgileri sakla
607
- product_info = [stock_amount, price_str, product_url, "", "", "", image_url]
608
 
609
  products.append((name, product_info, full_name))
610
 
@@ -613,6 +197,16 @@ print(f"Toplam {len(products)} ürün yüklendi.")
613
  # Initialize enhanced features
614
  initialize_enhanced_features(OPENAI_API_KEY, products)
615
 
 
 
 
 
 
 
 
 
 
 
616
  # Google Drive API ayarları
617
  GOOGLE_CREDENTIALS_PATH = os.getenv("GOOGLE_CREDENTIALS_PATH")
618
  GOOGLE_FOLDER_ID = "1bE8aMj8-eFGftjMPOF8bKQJAhfHa0BN8"
@@ -696,7 +290,7 @@ def run_scheduler(chat_history):
696
  schedule.run_pending()
697
  time.sleep(60)
698
 
699
- # Chatbot fonksiyonu - STOK API ENTEGRASYONU EKLENDİ
700
  def chatbot_fn(user_message, history, image=None):
701
  if history is None:
702
  history = []
@@ -712,8 +306,12 @@ def chatbot_fn(user_message, history, image=None):
712
  yield comparison_result
713
  return
714
 
 
 
 
715
  except Exception as e:
716
  print(f"Enhanced features error: {e}")
 
717
 
718
  # Log: Kullanıcı mesajını ekle
719
  try:
@@ -728,196 +326,95 @@ def chatbot_fn(user_message, history, image=None):
728
 
729
  # Döküman verilerini sistem mesajlarına ekle
730
  if document_content:
731
- system_messages.append({"role": "system", "content": f"Dökümanlardan gelen bilgiler: {document_content[:3000]}"}) # Token limiti için kısalt
732
 
733
- # STOK SORGUSU KONTROLÜ - YENİ EKLENEN KISIM
734
- if is_stock_query(user_message):
735
- print("Stok sorgusu algılandı, API'den veri çekiliyor...")
736
-
737
- # Mesajdan ürün adını çıkarmaya çalış
738
- # Basit bir yaklaşım: stok kelimelerini temizleyip kalan kelimeleri ürün adı olarak kullan
739
- product_words = []
740
- skip_words = ['stok', 'stock', 'kaç', 'adet', 'tane', 'var', 'mı', 'mi',
741
- 'mevcut', 'mu', 'bulunuyor', 'hangi', 'mağaza', 'nerede',
742
- 'durumu', 'stoklarda', 'stokta', 'için', 've', 'ile', 'toplam', 'toplamda',
743
- 'fiyat', 'fiyatı', 'ne', 'nedir', 'kadar', 'beden', 'bedeni', 'bedenli']
744
-
745
- # Beden terimleri - bunları skip etmemeliyiz
746
- size_terms = ['xs', 's', 'm', 'ml', 'l', 'xl', 'xxl', '2xl', '3xl', 'small', 'medium', 'large',
747
- '44', '46', '48', '50', '52', '54', '56', '58', '60']
748
-
749
- for word in user_message.lower().split():
750
- # Beden terimlerini veya skip_words dışındakileri ekle
751
- if word not in skip_words:
752
- product_words.append(word)
753
-
754
- if product_words:
755
- product_name = ' '.join(product_words)
756
-
757
- # Sadece beden sorgusu mu kontrol et (ör: "M", "XL", "52")
758
- size_terms = ['xs', 's', 'm', 'ml', 'l', 'xl', 'xxl', '2xl', '3xl', 'small', 'medium', 'large',
759
- '44', '46', '48', '50', '52', '54', '56', '58', '60']
760
- is_only_size = len(product_words) == 1 and product_words[0].lower() in size_terms
761
-
762
- if is_only_size:
763
- # Sadece beden sorgusu - tüm ürünlerde bu bedeni ara
764
- product_name = product_words[0].lower()
765
- print(f"Sadece beden sorgusu: {product_name}")
766
-
767
- # Kısa isimlerle arama yapıldıysa birden fazla ürün olabilir
768
- all_stock_info = []
769
-
770
- # Önce direkt API'de ara
771
- stock_info = get_realtime_stock_parallel(product_name)
772
- if stock_info:
773
- all_stock_info.append((product_name, stock_info))
774
-
775
- # Kısa arama ise (fx 3 gibi) benzer ürünleri de bul
776
- if len(product_words) <= 2 and 'fx' in product_name:
777
- # FX 3 varsa ara
778
- if '3' in product_name:
779
- fx3_info = get_realtime_stock_parallel('fx 3')
780
- if fx3_info:
781
- all_stock_info.append(('FX 3', fx3_info))
782
-
783
- # FX Sport AL 3 de ara
784
- fx_sport_info = get_realtime_stock_parallel('fx sport al 3')
785
- if fx_sport_info:
786
- all_stock_info.append(('FX Sport AL 3', fx_sport_info))
787
-
788
- # Eğer stok bilgisi bulunduysa ekle
789
- if all_stock_info:
790
- if len(all_stock_info) > 1:
791
- # Birden fazla ürün bulundu
792
- combined_info = "Eşleşen ürünler:\n"
793
- for prod_name, stock in all_stock_info:
794
- combined_info += f"\n{stock}\n---"
795
- system_messages.append({
796
- "role": "system",
797
- "content": f"GÜNCEL STOK BİLGİSİ (API'den alındı):\n{combined_info}"
798
- })
799
- else:
800
- # Tek ürün
801
- system_messages.append({
802
- "role": "system",
803
- "content": f"GÜNCEL STOK BİLGİSİ (API'den alındı):\n{all_stock_info[0][1]}"
804
- })
805
- print(f"Stok bilgisi eklendi")
806
 
807
- # XML'DEN FİYAT/GÖRSEL/LİNK BİLGİLERİ - Her zaman çalışır
808
- # Stok bilgisi API'den alındıysa, sadece fiyat/görsel/link için XML'e bak
809
- has_stock_from_api = is_stock_query(user_message) and 'all_stock_info' in locals() and len(all_stock_info) > 0
810
-
811
- # XML'den ürün bilgilerini al (fiyat, görsel, link için)
812
- # Her zaman orijinal mesajdaki kelimeleri kullan
813
- input_words = user_message.lower().split()
814
-
815
- added_products_count = 0
816
-
817
- # Eğer API'den stok alındıysa, önce tam ürün adıyla ara
818
- if has_stock_from_api and 'product_name' in locals():
819
- for product_info in products:
820
- product_full_name = product_info[2].lower()
821
- # FX Sport AL 3 tam eşleşme kontrolü
822
- if product_name.lower().replace(' ', '') in product_full_name.replace(' ', ''):
823
- print(f"XML tam eşleşme bulundu: {product_info[2]}")
824
- # Ürün bulundu, bilgileri al
825
- if len(product_info[1]) > 1 and product_info[1][1]:
826
- system_messages.append({
827
- "role": "system",
828
- "content": f"ÜRÜN BİLGİLERİ (XML):\nFiyat: {product_info[1][1]} TL\nLink: {product_info[1][2] if len(product_info[1]) > 2 else ''}\nResim: {product_info[1][6] if len(product_info[1]) > 6 else ''}"
829
- })
830
- added_products_count = 1
831
- break
832
 
833
- # Tam eşleşme bulunamadıysa normal kelime araması yap
834
- if added_products_count == 0:
 
 
835
  for word in input_words:
836
- if added_products_count >= 5: # Token limiti için maksimum 5 ürün
837
- break
838
  for product_info in products:
839
- # Basit kelime eşleştirme - eskisi gibi
840
  if word in product_info[0] or word in product_info[2].lower():
841
- # API'den stok alındıysa veya ürün stokta ise bilgileri al
842
- if has_stock_from_api or product_info[1][0] == "stokta":
843
- # Debug: hangi ürün bulundu
844
- print(f"XML'de bulunan ürün: {product_info[2]}")
845
-
846
- # Fiyat bilgisi varsa al
847
- if len(product_info[1]) > 1 and product_info[1][1]:
848
- normal_price = f"\\nFiyat: {product_info[1][1]} TL"
849
- print(f"Fiyat bulundu: {product_info[1][1]}")
850
- else:
851
- normal_price = ""
852
- print("Fiyat bilgisi yok")
853
-
854
- rebate_price = ""
855
- discount_info = ""
856
- eft_price = ""
857
- rebate_money_order_price = ""
858
 
859
- if len(product_info[1]) > 4 and product_info[1][4] and product_info[1][4] != "":
860
- rebate_price = f"\\nKampanyalı fiyat: {product_info[1][4]} TL"
861
-
862
- try:
863
- normal_price_float = float(product_info[1][1])
864
- rebate_price_float = float(product_info[1][4])
865
- discount_amount = normal_price_float - rebate_price_float
866
-
867
- if discount_amount > 0:
868
- if discount_amount > 200000:
869
- discount_amount_rounded = round(discount_amount / 5000) * 5000
870
- elif discount_amount > 30000:
871
- discount_amount_rounded = round(discount_amount / 1000) * 1000
872
- elif discount_amount > 10000:
873
- discount_amount_rounded = round(discount_amount / 100) * 100
874
- else:
875
- discount_amount_rounded = round(discount_amount / 10) * 10
876
-
877
- discount_info = f"\\nYapılan indirim: {discount_amount_rounded:.0f} TL"
878
- except (ValueError, TypeError):
879
- discount_info = ""
880
 
881
- rebate_money_order_price = ""
882
- else:
883
- if product_info[1][3] and product_info[1][3] != "":
884
- eft_price = f"\\nHavale indirimli fiyat: {product_info[1][3]} TL"
 
 
 
 
 
 
 
 
 
 
 
 
885
 
886
- product_link = f"\\nÜrün linki: {product_info[1][2]}"
887
- product_image = ""
888
- if len(product_info[1]) > 6 and product_info[1][6]:
889
- product_image = f"\\nÜrün resmi: {product_info[1][6]}"
890
-
891
- # API'den stok alındıysa sadece fiyat/görsel/link ekle
892
- if has_stock_from_api:
893
- new_msg = f"ÜRÜN BİLGİLERİ (Fiyat/Link):\\n{normal_price}{rebate_price}{discount_info}{eft_price}{rebate_money_order_price}{product_link}{product_image}"
894
- else:
895
- # API'den stok alınmadıysa hem stok hem fiyat bilgisi ekle
896
- 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}"
897
  else:
898
- # Stokta değilse
899
- if has_stock_from_api:
900
- # API'den stok bilgisi varsa XML'den stok bilgisi ekleme
901
- new_msg = None
902
- else:
903
- new_msg = f"{product_info[2]} {product_info[1][0]}"
904
 
905
- # Token limiti için mesaj boyutunu kontrol et
906
- if new_msg and len(new_msg) < 5000: # Maksimum 5000 karakter
907
- system_messages.append({"role": "system", "content": new_msg})
908
- added_products_count += 1
909
- break
910
-
911
- # Son 10 mesajla sınırla (token limiti için)
912
- if len(history) > 10:
913
- history = history[-10:]
 
 
 
914
 
915
  messages = system_messages + history + [{"role": "user", "content": user_message}]
916
 
917
  payload = {
918
- "model": "gpt-5-chat-latest",
919
  "messages": messages,
920
- "temperature": 0,
921
  "top_p": 1,
922
  "n": 1,
923
  "stream": True,
@@ -929,10 +426,9 @@ def chatbot_fn(user_message, history, image=None):
929
  "Authorization": f"Bearer {OPENAI_API_KEY}"
930
  }
931
 
932
- response = requests.post(API_URL, headers=headers, json=payload, stream=True, timeout=120)
933
  if response.status_code != 200:
934
- print(f"API Hatası: {response.status_code} - {response.text}")
935
- yield f"API Hatası ({response.status_code}): {response.text[:200]}"
936
  return
937
 
938
  partial_response = ""
@@ -959,6 +455,8 @@ def chatbot_fn(user_message, history, image=None):
959
  final_response = extract_product_info_for_gallery(partial_response)
960
  yield final_response
961
 
 
 
962
  # Log: Asistan cevabını ekle
963
  try:
964
  with file_lock:
@@ -1195,7 +693,9 @@ def enhanced_chatbot_fn(message, history, image):
1195
  # Demo arayüzü - Mobil responsive
1196
  with gr.Blocks(css=custom_css, theme="soft", title="Trek Asistanı", head=storage_js) as demo:
1197
  gr.Markdown("# 🚲 Trek Asistanı AI")
1198
- gr.Markdown("**Akıllı özellikler:** Gerçek zamanlı stok bilgisi, ürün karşılaştırması ve detaylı ürün bilgileri sunuyorum.")
 
 
1199
 
1200
  chatbot = gr.Chatbot(height=600, elem_id="chatbot", show_label=False)
1201
 
@@ -1205,6 +705,10 @@ with gr.Blocks(css=custom_css, theme="soft", title="Trek Asistanı", head=storag
1205
  elem_id="msg-input"
1206
  )
1207
 
 
 
 
 
1208
  def respond(message, chat_history):
1209
  if not message.strip():
1210
  return "", chat_history
@@ -1237,6 +741,8 @@ with gr.Blocks(css=custom_css, theme="soft", title="Trek Asistanı", head=storag
1237
  chat_history[-1] = (message, response)
1238
  yield "", chat_history
1239
 
 
 
1240
  except Exception as e:
1241
  error_msg = f"Üzgünüm, bir hata oluştu: {str(e)}"
1242
  print(f"Chat error: {e}")
 
6
  import schedule
7
  import time
8
  import threading
 
9
  from huggingface_hub import HfApi, create_repo, hf_hub_download
10
  import warnings
11
  import pandas as pd
 
29
  )
30
  from image_renderer import extract_product_info_for_gallery, format_message_with_images
31
 
32
+ # Import improved product search
33
+ try:
34
+ from improved_chatbot import ImprovedChatbot
35
+ USE_IMPROVED_SEARCH = True
36
+ except ImportError:
37
+ print("Improved chatbot not available, using basic search")
38
+ USE_IMPROVED_SEARCH = False
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39
 
40
  # Gradio uyarılarını bastır
41
  warnings.filterwarnings("ignore", category=UserWarning, module="gradio.components.chatbot")
 
52
 
53
  # Trek bisiklet ürünlerini çekme
54
  url = 'https://www.trekbisiklet.com.tr/output/8582384479'
55
+ response = requests.get(url, verify=False, timeout=30)
56
  if response.status_code == 200 and response.content:
57
  root = ET.fromstring(response.content)
58
  else:
 
187
  product_info.append("") # Boş resim URL'si
188
 
189
  else:
190
+ # Stokta olmayan ürün için sadece stok durumu
191
+ product_info = [stock_amount]
 
 
 
 
 
 
 
 
 
 
 
 
 
192
 
193
  products.append((name, product_info, full_name))
194
 
 
197
  # Initialize enhanced features
198
  initialize_enhanced_features(OPENAI_API_KEY, products)
199
 
200
+ # Initialize improved chatbot if available
201
+ improved_bot = None
202
+ if USE_IMPROVED_SEARCH:
203
+ try:
204
+ improved_bot = ImprovedChatbot(products)
205
+ print("Improved product search initialized successfully")
206
+ except Exception as e:
207
+ print(f"Failed to initialize improved search: {e}")
208
+ USE_IMPROVED_SEARCH = False
209
+
210
  # Google Drive API ayarları
211
  GOOGLE_CREDENTIALS_PATH = os.getenv("GOOGLE_CREDENTIALS_PATH")
212
  GOOGLE_FOLDER_ID = "1bE8aMj8-eFGftjMPOF8bKQJAhfHa0BN8"
 
290
  schedule.run_pending()
291
  time.sleep(60)
292
 
293
+ # Chatbot fonksiyonu
294
  def chatbot_fn(user_message, history, image=None):
295
  if history is None:
296
  history = []
 
306
  yield comparison_result
307
  return
308
 
309
+ # Enhanced features - Basit karşılaştırma ve görsel işleme
310
+ # Profil sistemi kaldırıldı - daha hızlı çalışma için
311
+
312
  except Exception as e:
313
  print(f"Enhanced features error: {e}")
314
+ # Enhanced features hata verirse normal chatbot'a devam et
315
 
316
  # Log: Kullanıcı mesajını ekle
317
  try:
 
326
 
327
  # Döküman verilerini sistem mesajlarına ekle
328
  if document_content:
329
+ system_messages.append({"role": "system", "content": f"Dökümanlardan gelen bilgiler: {document_content}"})
330
 
331
+ # Profil sistemi kaldırıldı - daha hızlı çalışma için
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
332
 
333
+ # Try improved search first if available
334
+ product_found_improved = False
335
+ if USE_IMPROVED_SEARCH and improved_bot:
336
+ try:
337
+ product_result = improved_bot.process_message(user_message)
338
+ if product_result['is_product_query'] and product_result['response']:
339
+ system_messages.append({
340
+ "role": "system",
341
+ "content": f"ÜRÜN BİLGİSİ:\n{product_result['response']}\n\nBu bilgileri kullanarak kullanıcıya yardımcı ol."
342
+ })
343
+ product_found_improved = True
344
+ except Exception as e:
345
+ print(f"Improved search error: {e}")
 
 
 
 
 
 
 
 
 
 
 
 
346
 
347
+ # Use basic search as fallback or if improved search didn't find anything
348
+ if not product_found_improved:
349
+ # Kullanıcı mesajında ürün ismi geçiyorsa ekle
350
+ input_words = user_message.lower().split()
351
  for word in input_words:
 
 
352
  for product_info in products:
 
353
  if word in product_info[0] or word in product_info[2].lower():
354
+ # Stokta olup olmadığını kontrol et
355
+ if product_info[1][0] == "stokta":
356
+ # Normal fiyat
357
+ normal_price = f"\\nFiyat: {product_info[1][1]} TL"
358
+
359
+ # Kampanyalı fiyat kontrolü
360
+ rebate_price = ""
361
+ discount_info = ""
362
+ eft_price = ""
363
+ rebate_money_order_price = ""
364
+
365
+ # Kampanyalı fiyat var mı kontrol et
366
+ if len(product_info[1]) > 4 and product_info[1][4] and product_info[1][4] != "":
367
+ rebate_price = f"\\nKampanyalı fiyat: {product_info[1][4]} TL"
 
 
 
368
 
369
+ # İndirim miktarını hesapla
370
+ try:
371
+ normal_price_float = float(product_info[1][1])
372
+ rebate_price_float = float(product_info[1][4])
373
+ discount_amount = normal_price_float - rebate_price_float
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
374
 
375
+ # İndirim miktarı 0'dan büyükse göster
376
+ if discount_amount > 0:
377
+ # İndirim miktarı için yuvarlama kurallarını uygula
378
+ if discount_amount > 200000:
379
+ discount_amount_rounded = round(discount_amount / 5000) * 5000
380
+ elif discount_amount > 30000:
381
+ discount_amount_rounded = round(discount_amount / 1000) * 1000
382
+ elif discount_amount > 10000:
383
+ discount_amount_rounded = round(discount_amount / 100) * 100
384
+ else:
385
+ discount_amount_rounded = round(discount_amount / 10) * 10
386
+
387
+ # İndirim bilgisi
388
+ discount_info = f"\\nYapılan indirim: {discount_amount_rounded:.0f} TL"
389
+ except (ValueError, TypeError):
390
+ discount_info = ""
391
 
392
+ # Havale indirimli kampanyalı fiyat bilgisini gösterme
393
+ rebate_money_order_price = ""
 
 
 
 
 
 
 
 
 
394
  else:
395
+ # Kampanyalı değilse, havale indirimli normal fiyatı göster
396
+ if product_info[1][3] and product_info[1][3] != "":
397
+ eft_price = f"\\nHavale indirimli fiyat: {product_info[1][3]} TL"
 
 
 
398
 
399
+ # Ürün linki ve resim
400
+ product_link = f"\\nÜrün linki: {product_info[1][2]}"
401
+ product_image = ""
402
+ if len(product_info[1]) > 6 and product_info[1][6]: # Resim URL'si varsa
403
+ product_image = f"\\nÜrün resmi: {product_info[1][6]}"
404
+
405
+ # Tüm bilgileri birleştir
406
+ 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}"
407
+ else:
408
+ # Ürün stokta yoksa sadece stok durumunu bildir
409
+ new_msg = f"{product_info[2]} {product_info[1][0]}"
410
+ system_messages.append({"role": "system", "content": new_msg})
411
 
412
  messages = system_messages + history + [{"role": "user", "content": user_message}]
413
 
414
  payload = {
415
+ "model": "gpt-4.1",
416
  "messages": messages,
417
+ "temperature": 0.2,
418
  "top_p": 1,
419
  "n": 1,
420
  "stream": True,
 
426
  "Authorization": f"Bearer {OPENAI_API_KEY}"
427
  }
428
 
429
+ response = requests.post(API_URL, headers=headers, json=payload, stream=True)
430
  if response.status_code != 200:
431
+ yield "Bir hata oluştu."
 
432
  return
433
 
434
  partial_response = ""
 
455
  final_response = extract_product_info_for_gallery(partial_response)
456
  yield final_response
457
 
458
+ # Profil kaydetme kaldırıldı - daha hızlı çalışma için
459
+
460
  # Log: Asistan cevabını ekle
461
  try:
462
  with file_lock:
 
693
  # Demo arayüzü - Mobil responsive
694
  with gr.Blocks(css=custom_css, theme="soft", title="Trek Asistanı", head=storage_js) as demo:
695
  gr.Markdown("# 🚲 Trek Asistanı AI")
696
+ gr.Markdown("**Akıllı özellikler:** Ürün karşılaştırması ve detaylı ürün bilgileri sunuyorum.")
697
+
698
+ # LocalStorage fonksiyonu kaldırıldı
699
 
700
  chatbot = gr.Chatbot(height=600, elem_id="chatbot", show_label=False)
701
 
 
705
  elem_id="msg-input"
706
  )
707
 
708
+ # Restore button tamamen kaldırıldı
709
+
710
+ # Session ve profil sistemi tamamen kaldırıldı
711
+
712
  def respond(message, chat_history):
713
  if not message.strip():
714
  return "", chat_history
 
741
  chat_history[-1] = (message, response)
742
  yield "", chat_history
743
 
744
+ # Chat kaydetme sistemi kaldırıldı
745
+
746
  except Exception as e:
747
  error_msg = f"Üzgünüm, bir hata oluştu: {str(e)}"
748
  print(f"Chat error: {e}")