SamiKoen commited on
Commit
2f98d6a
·
verified ·
1 Parent(s): c8b81e5

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +94 -630
app.py CHANGED
@@ -15,7 +15,6 @@ from google.oauth2.service_account import Credentials
15
  from googleapiclient.discovery import build
16
  from googleapiclient.http import MediaIoBaseDownload
17
  import io
18
- import warnings
19
  from requests.packages.urllib3.exceptions import InsecureRequestWarning
20
  warnings.simplefilter('ignore', InsecureRequestWarning)
21
 
@@ -32,7 +31,26 @@ from image_renderer import extract_product_info_for_gallery, format_message_with
32
  # Import conversation tracker
33
  from conversation_tracker import add_conversation
34
 
35
- # API endpoints removed - will use Gradio's built-in API
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
 
37
  # Import smart warehouse with GPT intelligence and price
38
  try:
@@ -60,7 +78,6 @@ def get_warehouse_stock_old(product_name):
60
  """Smart warehouse stock finder with general algorithm"""
61
  try:
62
  import re
63
- import requests
64
 
65
  # Get XML with retry
66
  xml_text = None
@@ -98,42 +115,26 @@ def get_warehouse_stock_old(product_name):
98
 
99
  # If query is very short (like "hangi boyu"), skip it
100
  if len(words) <= 2 and not any(w.isdigit() for w in words):
101
- # Likely just a question, not a product search
102
  pass
103
  else:
104
- # Extract product-like terms
105
  for word in words:
106
- # Skip if it's a size marker
107
  if word in sizes:
108
  continue
109
-
110
- # Always keep numbers (model numbers)
111
  if word.isdigit():
112
  product_words.append(word)
113
-
114
- # Keep alphanumeric codes
115
  elif any(c.isdigit() for c in word) and any(c.isalpha() for c in word):
116
  product_words.append(word)
117
-
118
- # Keep 2-3 letter codes that look like product codes
119
  elif len(word) in [2, 3] and word.isalpha():
120
- # Skip common particles
121
  if word in ['mi', 'mı', 'mu', 'mü', 'var', 'yok', 've', 'de', 'da']:
122
  continue
123
- # Must have at least one consonant
124
  if any(c not in 'aeiouı' for c in word):
125
  product_words.append(word)
126
-
127
- # For longer words, be selective
128
  elif len(word) > 3:
129
- # Skip if ends with Turkish question suffixes
130
  if any(word.endswith(suffix) for suffix in ['mi', 'mı', 'mu', 'mü']):
131
  continue
132
- # Skip if only 1-2 consonants (likely a particle/question word)
133
  consonants = sum(1 for c in word if c not in 'aeiouı')
134
- if consonants <= 2: # "var" has 2 consonants, skip it
135
  continue
136
- # Keep it
137
  product_words.append(word)
138
 
139
  print(f"DEBUG - Searching: {' '.join(product_words)}, Size: {size}")
@@ -144,11 +145,8 @@ def get_warehouse_stock_old(product_name):
144
 
145
  print(f"DEBUG - Total products in XML: {len(all_products)}")
146
 
147
- # Search through products
148
  best_match = None
149
-
150
  for product_block in all_products:
151
- # Extract product name
152
  name_match = re.search(r'<ProductName><!\[CDATA\[(.*?)\]\]></ProductName>', product_block)
153
  if not name_match:
154
  continue
@@ -156,37 +154,27 @@ def get_warehouse_stock_old(product_name):
156
  product_name_in_xml = name_match.group(1)
157
  normalized_xml_name = normalize(product_name_in_xml)
158
 
159
- # Check if all product words are in the name (as separate words or part of words)
160
- # This handles cases like "gen 8" where it might be "gen8" or "gen 8" in XML
161
  match = True
162
  for word in product_words:
163
- # Check if word exists as-is or without spaces (for numbers after text)
164
  if word not in normalized_xml_name:
165
- # Also check if it's a number that might be attached to previous word
166
  if not (word.isdigit() and any(f"{prev}{word}" in normalized_xml_name or f"{prev} {word}" in normalized_xml_name for prev in product_words if not prev.isdigit())):
167
  match = False
168
  break
169
 
170
  if match:
171
- # Product name matches, now check variant if needed
172
-
173
  if size:
174
- # Check if variant matches the size
175
  variant_match = re.search(r'<ProductVariant><!\[CDATA\[(.*?)\]\]></ProductVariant>', product_block)
176
  if variant_match:
177
  variant = variant_match.group(1)
178
- # Check if variant starts with the size (e.g., "S-BEYAZ")
179
  if variant.upper().startswith(f'{size.upper()}-'):
180
  print(f"DEBUG - Found match: {product_name_in_xml} - {variant}")
181
  best_match = product_block
182
- break # Found exact match, stop searching
183
  else:
184
- # No size specified, take first match
185
  best_match = product_block
186
  break
187
 
188
  if best_match:
189
- # Extract warehouse info from the matched product
190
  warehouse_info = []
191
  warehouse_regex = r'<Warehouse>.*?<Name><!\[CDATA\[(.*?)\]\]></Name>.*?<Stock>(.*?)</Stock>.*?</Warehouse>'
192
  warehouses = re.findall(warehouse_regex, best_match, re.DOTALL)
@@ -195,7 +183,6 @@ def get_warehouse_stock_old(product_name):
195
  try:
196
  stock = int(wh_stock.strip())
197
  if stock > 0:
198
- # Format store names
199
  if "CADDEBOSTAN" in wh_name:
200
  display = "Caddebostan mağazası"
201
  elif "ORTAKÖY" in wh_name:
@@ -206,7 +193,6 @@ def get_warehouse_stock_old(product_name):
206
  display = "Bahçeköy mağazası"
207
  else:
208
  display = wh_name
209
-
210
  warehouse_info.append(f"{display}: Mevcut")
211
  except:
212
  pass
@@ -220,205 +206,6 @@ def get_warehouse_stock_old(product_name):
220
  print(f"Warehouse error: {e}")
221
  return None
222
 
223
- # OLD SLOW VERSION - KEEP FOR REFERENCE
224
- def get_warehouse_stock_old_slow(product_name):
225
- """B2B API'den mağaza stok bilgilerini çek - Optimize edilmiş versiyon"""
226
- try:
227
- import re
228
-
229
- # Hugging Face'de signal çalışmadığı için try-except kullan
230
- use_signal = False
231
- try:
232
- import signal
233
- import threading
234
- # Test if we're in main thread
235
- if threading.current_thread() is threading.main_thread():
236
- def timeout_handler(signum, frame):
237
- raise TimeoutError("Warehouse API timeout")
238
- signal.signal(signal.SIGALRM, timeout_handler)
239
- signal.alarm(8)
240
- use_signal = True
241
- except Exception as e:
242
- print(f"Signal not available: {e}")
243
- use_signal = False
244
-
245
- try:
246
- warehouse_url = 'https://video.trek-turkey.com/bizimhesap-warehouse-xml-b2b-api-v2.php'
247
- response = requests.get(warehouse_url, verify=False, timeout=7) # Increased from 2
248
-
249
- if response.status_code != 200:
250
- return None
251
-
252
- # ULTRA FAST: Use regex instead of XML parsing for speed
253
- xml_text = response.text
254
-
255
- # Turkish character normalization function
256
- turkish_map = {'ı': 'i', 'ğ': 'g', 'ü': 'u', 'ş': 's', 'ö': 'o', 'ç': 'c', 'İ': 'i', 'I': 'i'}
257
-
258
- def normalize_turkish(text):
259
- import unicodedata
260
- # First normalize unicode to handle combining characters
261
- text = unicodedata.normalize('NFKD', text)
262
- text = ''.join(char for char in text if unicodedata.category(char) != 'Mn')
263
- # Replace Turkish characters
264
- for tr_char, en_char in turkish_map.items():
265
- text = text.replace(tr_char, en_char)
266
- # Also handle dotted i (İ with dot above)
267
- text = text.replace('İ', 'i').replace('I', 'i')
268
- return text.lower()
269
-
270
- # Normalize search product name
271
- search_name = normalize_turkish(product_name.strip())
272
- # Gen 8, Gen 7 vs. ifadelerini koruyalım
273
- search_name = search_name.replace('(2026)', '').replace('(2025)', '').strip()
274
- search_words = search_name.split()
275
-
276
- print(f"DEBUG - Searching for: {product_name}")
277
- print(f"DEBUG - Normalized (gen kept): {search_name}")
278
-
279
- # SUPER FAST SEARCH - Build index once
280
- # Create a dictionary for O(1) lookup instead of O(n) search
281
- product_index = {}
282
-
283
- # Build index in one pass
284
- for product in root.findall('Product'):
285
- name_elem = product.find('ProductName')
286
- if name_elem is not None and name_elem.text:
287
- product_name_key = normalize_turkish(name_elem.text.strip())
288
-
289
- # Store all products with same name in a list
290
- if product_name_key not in product_index:
291
- product_index[product_name_key] = []
292
-
293
- variant_elem = product.find('ProductVariant')
294
- variant_text = ""
295
- if variant_elem is not None and variant_elem.text:
296
- variant_text = normalize_turkish(variant_elem.text.strip())
297
-
298
- product_index[product_name_key].append({
299
- 'element': product,
300
- 'original_name': name_elem.text.strip(),
301
- 'variant': variant_text
302
- })
303
-
304
- # Separate size/variant words from product words
305
- size_color_words = ['s', 'm', 'l', 'xl', 'xs', 'xxl', 'ml']
306
- size_indicators = ['beden', 'size', 'boy']
307
-
308
- variant_words = [word for word in search_words if word in size_color_words]
309
- product_words = [word for word in search_words if word not in size_color_words and word not in size_indicators]
310
-
311
- # Build the product key to search
312
- product_key = ' '.join(product_words)
313
-
314
- print(f"DEBUG - Looking for key: {product_key}")
315
- print(f"DEBUG - Variant filter: {variant_words}")
316
-
317
- # INSTANT LOOKUP - O(1)
318
- candidates = []
319
-
320
- # Try exact match first
321
- if product_key in product_index:
322
- print(f"DEBUG - Exact match found!")
323
- for item in product_index[product_key]:
324
- # Check variant
325
- if variant_words:
326
- # For size codes like S, M, L
327
- if len(variant_words) == 1 and len(variant_words[0]) <= 2:
328
- if item['variant'].startswith(variant_words[0] + '-') or item['variant'].startswith(variant_words[0] + ' '):
329
- candidates.append((item['element'], item['original_name'], item['variant']))
330
- else:
331
- if all(word in item['variant'] for word in variant_words):
332
- candidates.append((item['element'], item['original_name'], item['variant']))
333
- else:
334
- # No variant filter, add all
335
- candidates.append((item['element'], item['original_name'], item['variant']))
336
- else:
337
- # Fallback: search all keys that contain all product words
338
- print(f"DEBUG - No exact match, searching partial matches...")
339
- for key, items in product_index.items():
340
- if all(word in key for word in product_words):
341
- for item in items:
342
- if variant_words:
343
- if len(variant_words) == 1 and len(variant_words[0]) <= 2:
344
- if item['variant'].startswith(variant_words[0] + '-'):
345
- candidates.append((item['element'], item['original_name'], item['variant']))
346
- else:
347
- candidates.append((item['element'], item['original_name'], item['variant']))
348
-
349
- if len(candidates) >= 5: # Found enough
350
- break
351
-
352
- print(f"DEBUG - Found {len(candidates)} candidates instantly!")
353
-
354
- # Collect stock info from new structure
355
- warehouse_stock_map = {}
356
-
357
- for product, xml_name, variant in candidates:
358
- # New structure: Warehouse elements are direct children of Product
359
- for warehouse in product.findall('Warehouse'):
360
- name_elem = warehouse.find('Name')
361
- stock_elem = warehouse.find('Stock')
362
-
363
- if name_elem is not None and stock_elem is not None:
364
- warehouse_name = name_elem.text if name_elem.text else "Bilinmeyen"
365
- try:
366
- stock_count = int(stock_elem.text) if stock_elem.text else 0
367
- if stock_count > 0:
368
- if warehouse_name in warehouse_stock_map:
369
- warehouse_stock_map[warehouse_name] += stock_count
370
- else:
371
- warehouse_stock_map[warehouse_name] = stock_count
372
- except (ValueError, TypeError):
373
- pass
374
-
375
- # Cancel alarm
376
- signal.alarm(0)
377
-
378
- print(f"DEBUG - Found {len(candidates)} candidates")
379
- if candidates:
380
- for product, name, variant in candidates[:3]:
381
- print(f"DEBUG - Candidate: {name} - {variant}")
382
-
383
- if warehouse_stock_map:
384
- all_warehouse_info = []
385
- for warehouse_name, total_stock in warehouse_stock_map.items():
386
- # Mağaza isimlerini daha anlaşılır hale getir
387
- if "Caddebostan" in warehouse_name:
388
- display_name = "Caddebostan mağazası"
389
- elif "Ortaköy" in warehouse_name:
390
- display_name = "Ortaköy mağazası"
391
- elif "Sarıyer" in warehouse_name:
392
- display_name = "Sarıyer mağazası"
393
- elif "Alsancak" in warehouse_name or "İzmir" in warehouse_name:
394
- display_name = "İzmir Alsancak mağazası"
395
- else:
396
- display_name = warehouse_name
397
-
398
- all_warehouse_info.append(f"{display_name}: Mevcut")
399
- return all_warehouse_info
400
- else:
401
- return ["Hiçbir mağazada mevcut değil"]
402
-
403
- except TimeoutError:
404
- if use_signal:
405
- try:
406
- signal.alarm(0)
407
- except:
408
- pass
409
- print("Warehouse API timeout - skipping")
410
- return None
411
- finally:
412
- if use_signal:
413
- try:
414
- signal.alarm(0)
415
- except:
416
- pass
417
-
418
- except Exception as e:
419
- print(f"Mağaza stok bilgisi çekme hatası: {e}")
420
- return None
421
-
422
  # Import improved product search
423
  try:
424
  from improved_chatbot import ImprovedChatbot
@@ -453,12 +240,10 @@ else:
453
  products = []
454
  if root is not None:
455
  for item in root.findall('item'):
456
- # Tüm ürünleri al, sonra stokta olma durumunu kontrol et - None kontrolü
457
  rootlabel_elem = item.find('rootlabel')
458
  stock_elem = item.find('stockAmount')
459
-
460
  if rootlabel_elem is None or stock_elem is None:
461
- continue # Eksik veri varsa bu ürünü atla
462
 
463
  name_words = rootlabel_elem.text.lower().split()
464
  name = name_words[0]
@@ -466,56 +251,38 @@ if root is not None:
466
 
467
  stock_amount = "stokta" if stock_elem.text and stock_elem.text > '0' else "stokta değil"
468
 
469
- # Stokta olmayan ürünler için fiyat/link bilgisi eklemiyoruz
470
  if stock_amount == "stokta":
471
- # Normal fiyat bilgisini al - Güvenli versiyon
472
  price_elem = item.find('priceTaxWithCur')
473
  price_str = price_elem.text if price_elem is not None and price_elem.text else "Fiyat bilgisi yok"
474
-
475
- # EFT fiyatını al (havale indirimli orijinal fiyat) - Güvenli versiyon
476
  price_eft_elem = item.find('priceEft')
477
  price_eft_str = price_eft_elem.text if price_eft_elem is not None and price_eft_elem.text else ""
478
-
479
- # İndirimli fiyatı al (kampanyalı fiyat) - Güvenli versiyon
480
  price_rebate_elem = item.find('priceRebateWithTax')
481
  price_rebate_str = price_rebate_elem.text if price_rebate_elem is not None and price_rebate_elem.text else ""
482
-
483
- # Havale indirimi fiyatını al (havale indirimli kampanyalı fiyat) - Güvenli versiyon
484
  price_rebate_money_order_elem = item.find('priceRebateWithMoneyOrderWithTax')
485
  price_rebate_money_order_str = price_rebate_money_order_elem.text if price_rebate_money_order_elem is not None and price_rebate_money_order_elem.text else ""
486
 
487
- # Normal fiyatı yuvarla
488
  try:
489
  price_float = float(price_str)
490
- # Fiyat 200000 üzerindeyse en yakın 5000'lik basamağa yuvarla
491
  if price_float > 200000:
492
- price = str(round(price_float / 5000) * 5000) # En yakın 5000'e yuvarla
493
- # Fiyat 30000 üzerindeyse en yakın 1000'lik basamağa yuvarla
494
  elif price_float > 30000:
495
- price = str(round(price_float / 1000) * 1000) # En yakın 1000'e yuvarla
496
- # Fiyat 10000 üzerindeyse en yakın 100'lük basamağa yuvarla
497
  elif price_float > 10000:
498
- price = str(round(price_float / 100) * 100) # En yakın 100'e yuvarla
499
- # Diğer durumlarda en yakın 10'luk basamağa yuvarla
500
  else:
501
- price = str(round(price_float / 10) * 10) # En yakın 10'a yuvarla
502
  except (ValueError, TypeError):
503
- price = price_str # Sayıya dönüştürülemezse olduğu gibi bırak
504
 
505
- # Havale indirimli orijinal fiyatı yuvarla (varsa)
506
  if price_eft_str:
507
  try:
508
  price_eft_float = float(price_eft_str)
509
- # Fiyat 200000 üzerindeyse en yakın 5000'lik basamağa yuvarla
510
  if price_eft_float > 200000:
511
  price_eft = str(round(price_eft_float / 5000) * 5000)
512
- # Fiyat 30000 üzerindeyse en yakın 1000'lik basamağa yuvarla
513
  elif price_eft_float > 30000:
514
  price_eft = str(round(price_eft_float / 1000) * 1000)
515
- # Fiyat 10000 üzerindeyse en yakın 100'lük basamağa yuvarla
516
  elif price_eft_float > 10000:
517
  price_eft = str(round(price_eft_float / 100) * 100)
518
- # Diğer durumlarda en yakın 10'luk basamağa yuvarla
519
  else:
520
  price_eft = str(round(price_eft_float / 10) * 10)
521
  except (ValueError, TypeError):
@@ -523,20 +290,15 @@ if root is not None:
523
  else:
524
  price_eft = ""
525
 
526
- # İndirimli fiyatı yuvarla (varsa)
527
  if price_rebate_str:
528
  try:
529
  price_rebate_float = float(price_rebate_str)
530
- # Fiyat 200000 üzerindeyse en yakın 5000'lik basamağa yuvarla
531
  if price_rebate_float > 200000:
532
  price_rebate = str(round(price_rebate_float / 5000) * 5000)
533
- # Fiyat 30000 üzerindeyse en yakın 1000'lik basamağa yuvarla
534
  elif price_rebate_float > 30000:
535
  price_rebate = str(round(price_rebate_float / 1000) * 1000)
536
- # Fiyat 10000 üzerindeyse en yakın 100'lük basamağa yuvarla
537
  elif price_rebate_float > 10000:
538
  price_rebate = str(round(price_rebate_float / 100) * 100)
539
- # Diğer durumlarda en yakın 10'luk basamağa yuvarla
540
  else:
541
  price_rebate = str(round(price_rebate_float / 10) * 10)
542
  except (ValueError, TypeError):
@@ -544,20 +306,15 @@ if root is not None:
544
  else:
545
  price_rebate = ""
546
 
547
- # Havale indirimli kampanyalı fiyatı yuvarla (varsa)
548
  if price_rebate_money_order_str:
549
  try:
550
  price_rebate_money_order_float = float(price_rebate_money_order_str)
551
- # Fiyat 200000 üzerindeyse en yakın 5000'lik basamağa yuvarla
552
  if price_rebate_money_order_float > 200000:
553
  price_rebate_money_order = str(round(price_rebate_money_order_float / 5000) * 5000)
554
- # Fiyat 30000 üzerindeyse en yakın 1000'lik basamağa yuvarla
555
  elif price_rebate_money_order_float > 30000:
556
  price_rebate_money_order = str(round(price_rebate_money_order_float / 1000) * 1000)
557
- # Fiyat 10000 üzerindeyse en yakın 100'lük basamağa yuvarla
558
  elif price_rebate_money_order_float > 10000:
559
  price_rebate_money_order = str(round(price_rebate_money_order_float / 100) * 100)
560
- # Diğer durumlarda en yakın 10'luk basamağa yuvarla
561
  else:
562
  price_rebate_money_order = str(round(price_rebate_money_order_float / 10) * 10)
563
  except (ValueError, TypeError):
@@ -565,20 +322,16 @@ if root is not None:
565
  else:
566
  price_rebate_money_order = ""
567
 
568
- # Ürün bilgilerini al - None kontrolü ekle
569
  product_url_elem = item.find('productLink')
570
  product_url = product_url_elem.text if product_url_elem is not None and product_url_elem.text else ""
571
  product_info = [stock_amount, price, product_url, price_eft, price_rebate, price_rebate_money_order]
572
 
573
- # Resim URL'si ekle (varsa) - Güvenli versiyon
574
  image_elem = item.find('picture1Path')
575
  if image_elem is not None and image_elem.text:
576
  product_info.append(image_elem.text)
577
  else:
578
- product_info.append("") # Boş resim URL'si
579
-
580
  else:
581
- # Stokta olmayan ürün için sadece stok durumu
582
  product_info = [stock_amount]
583
 
584
  products.append((name, product_info, full_name))
@@ -588,6 +341,9 @@ print(f"Toplam {len(products)} ürün yüklendi.")
588
  # Initialize enhanced features
589
  initialize_enhanced_features(OPENAI_API_KEY, products)
590
 
 
 
 
591
  # Initialize improved chatbot if available
592
  improved_bot = None
593
  if USE_IMPROVED_SEARCH:
@@ -620,7 +376,6 @@ def download_documents_from_drive():
620
  credentials = Credentials.from_service_account_file(GOOGLE_CREDENTIALS_PATH)
621
  service = build('drive', 'v3', credentials=credentials)
622
 
623
- # Klasördeki dosyaları listele
624
  results = service.files().list(
625
  q=f"'{GOOGLE_FOLDER_ID}' in parents",
626
  fields="files(id, name, mimeType)"
@@ -631,8 +386,6 @@ def download_documents_from_drive():
631
 
632
  for file in files:
633
  print(f"İndiriliyor: {file['name']}")
634
-
635
- # DOCX dosyaları için
636
  if file['mimeType'] == 'application/vnd.openxmlformats-officedocument.wordprocessingml.document':
637
  request = service.files().get_media(fileId=file['id'])
638
  file_io = io.BytesIO()
@@ -645,14 +398,14 @@ def download_documents_from_drive():
645
  file_io.seek(0)
646
  doc = Document(file_io)
647
 
648
- content = f"\\n=== {file['name']} ===\\n"
649
  for paragraph in doc.paragraphs:
650
  if paragraph.text.strip():
651
- content += paragraph.text + "\\n"
652
 
653
  all_content.append(content)
654
 
655
- document_content = "\\n".join(all_content)
656
  print(f"Toplam {len(files)} döküman yüklendi.")
657
 
658
  except Exception as e:
@@ -668,7 +421,7 @@ def clear_log_file():
668
  if os.path.exists(LOG_FILE):
669
  with file_lock:
670
  with open(LOG_FILE, 'w', encoding='utf-8') as f:
671
- f.write("Log dosyası temizlendi.\\n")
672
  print("Log dosyası temizlendi.")
673
  except Exception as e:
674
  print(f"Log dosyası temizleme hatası: {e}")
@@ -676,7 +429,6 @@ def clear_log_file():
676
  # Zamanlanmış görevleri çalıştırma fonksiyonu
677
  def run_scheduler(chat_history):
678
  schedule.every().day.at("03:00").do(clear_log_file)
679
-
680
  while True:
681
  schedule.run_pending()
682
  time.sleep(60)
@@ -686,63 +438,46 @@ def chatbot_fn(user_message, history, image=None):
686
  if history is None:
687
  history = []
688
 
689
- # ÖNCELİKLE warehouse stock bilgisini al (streaming başlamadan önce)
690
  warehouse_stock_data = None
691
  print(f"DEBUG - Getting warehouse stock FIRST for: {user_message}")
692
  try:
693
  warehouse_stock_data = get_warehouse_stock(user_message)
694
  if warehouse_stock_data:
695
- print(f"DEBUG - Warehouse stock found: {warehouse_stock_data[:2]}...") # İlk 2 mağaza
696
  else:
697
  print(f"DEBUG - No warehouse stock data returned")
698
  except Exception as e:
699
  print(f"DEBUG - Warehouse stock error at start: {e}")
700
 
701
  try:
702
- # Enhanced features - Görsel işleme
703
  if image is not None:
704
  user_message = process_image_message(image, user_message)
705
 
706
- # Enhanced features - Karşılaştırma kontrolü
707
  comparison_result = handle_comparison_request(user_message)
708
  if comparison_result:
709
  yield comparison_result
710
  return
711
-
712
- # Enhanced features - Basit karşılaştırma ve görsel işleme
713
- # Profil sistemi kaldırıldı - daha hızlı çalışma için
714
-
715
  except Exception as e:
716
  print(f"Enhanced features error: {e}")
717
- # Enhanced features hata verirse normal chatbot'a devam et
718
 
719
- # Log: Kullanıcı mesajını ekle
720
  try:
721
  with file_lock:
722
  with open(LOG_FILE, 'a', encoding='utf-8') as f:
723
- f.write(f"User: {user_message}\\n")
724
  except Exception as e:
725
  print(f"Dosya yazma hatası (Kullanıcı): {e}")
726
 
727
- # Sistem mesajlarını external dosyadan yükle
728
  system_messages = get_prompt_content_only()
729
 
730
- # Döküman verilerini sistem mesajlarına ekle
731
  if document_content:
732
  system_messages.append({"role": "system", "content": f"Dökümanlardan gelen bilgiler: {document_content}"})
733
 
734
- # Profil sistemi kaldırıldı - daha hızlı çalışma için
735
-
736
- # Try improved search first if available
737
  product_found_improved = False
738
  if USE_IMPROVED_SEARCH and improved_bot:
739
  try:
740
  product_result = improved_bot.process_message(user_message)
741
  if product_result['is_product_query'] and product_result['response']:
742
- # Extract product name from improved search result for warehouse stock
743
  enhanced_response = product_result['response']
744
-
745
- # Önceden alınmış warehouse stock bilgisini kullan
746
  if warehouse_stock_data and warehouse_stock_data != ["Hiçbir mağazada mevcut değil"]:
747
  warehouse_info = f"\n\n🏪 MAĞAZA STOK BİLGİLERİ:\n"
748
  for store_info in warehouse_stock_data:
@@ -761,11 +496,8 @@ def chatbot_fn(user_message, history, image=None):
761
  except Exception as e:
762
  print(f"Improved search error: {e}")
763
 
764
- # Only use warehouse stock data if available
765
  if not product_found_improved:
766
  print(f"DEBUG chatbot_fn - Using warehouse stock data for: {user_message}")
767
-
768
- # Warehouse stock bilgisi varsa kullan
769
  if warehouse_stock_data and warehouse_stock_data != ["Hiçbir mağazada mevcut değil"]:
770
  warehouse_info = f"🏪 MAĞAZA STOK BİLGİLERİ:\n"
771
  for store_info in warehouse_stock_data:
@@ -787,7 +519,7 @@ def chatbot_fn(user_message, history, image=None):
787
  messages = system_messages + history + [{"role": "user", "content": user_message}]
788
 
789
  payload = {
790
- "model": "gpt-5-chat-latest",
791
  "messages": messages,
792
  "temperature": 0.2,
793
  "top_p": 1,
@@ -818,7 +550,6 @@ def chatbot_fn(user_message, history, image=None):
818
  delta = chunk_data['choices'][0]['delta']
819
  if 'content' in delta:
820
  partial_response += delta['content']
821
- # Resim formatlaması uygula
822
  formatted_response = extract_product_info_for_gallery(partial_response)
823
  yield formatted_response
824
  except json.JSONDecodeError as e:
@@ -826,21 +557,16 @@ def chatbot_fn(user_message, history, image=None):
826
  elif chunk_str == "data: [DONE]":
827
  break
828
 
829
- # Son resim formatlaması
830
  final_response = extract_product_info_for_gallery(partial_response)
831
  yield final_response
832
 
833
- # Profil kaydetme kaldırıldı - daha hızlı çalışma için
834
-
835
- # Log: Asistan cevabını ekle
836
  try:
837
  with file_lock:
838
  with open(LOG_FILE, 'a', encoding='utf-8') as f:
839
- f.write(f"Bot: {partial_response}\\n")
840
  except Exception as e:
841
  print(f"Dosya yazma hatası (Bot): {e}")
842
 
843
- # Global geçmişi güncelle
844
  with history_lock:
845
  global_chat_history.append({"role": "user", "content": user_message})
846
  global_chat_history.append({"role": "assistant", "content": partial_response})
@@ -859,219 +585,29 @@ if not USE_SLOW_ECHO:
859
  scheduler_thread = threading.Thread(target=run_scheduler, args=(global_chat_history,), daemon=True)
860
  scheduler_thread.start()
861
 
862
- # Trek markasına özel tema oluştur (düzeltilmiş sürüm)
863
  trek_theme = gr.themes.Base(
864
- primary_hue="red", # Trek kırmızısı için
865
- secondary_hue="slate", # Gri tonları için
866
  neutral_hue="slate",
867
- radius_size=gr.themes.sizes.radius_sm, # Köşe yuvarlatma değerleri
868
- spacing_size=gr.themes.sizes.spacing_md, # Aralık değerleri
869
- text_size=gr.themes.sizes.text_sm # Yazı boyutları (small)
870
  )
871
- # Chatbot kartları için arka plan renkleri değiştiren CSS
872
  custom_css = """
873
- /* Genel font ayarları */
874
- .gradio-container, .gradio-container *, .message-wrap, .message-wrap p, .message-wrap div,
875
- button, input, select, textarea {
876
- font-family: 'Segoe UI', 'SF Pro Text', 'Roboto', -apple-system, BlinkMacSystemFont,
877
- 'Helvetica Neue', Arial, sans-serif !important;
878
- font-size: 0.6rem !important;
879
- }
880
-
881
- /* Mobil responsive başlangıç */
882
- @media (max-width: 768px) {
883
- .gradio-container, .gradio-container *, .message-wrap, .message-wrap p, .message-wrap div {
884
- font-size: 0.8rem !important;
885
- }
886
-
887
- h1 {
888
- font-size: 1.8rem !important;
889
- text-align: center;
890
- margin: 10px 0;
891
- }
892
-
893
- h2 {
894
- font-size: 1.4rem !important;
895
- }
896
- }
897
-
898
- /* Input alanı için de aynı boyut */
899
- .message-textbox textarea {
900
- font-size: 0.6rem !important;
901
- }
902
-
903
- /* Başlıklar için özel boyutlar */
904
- h1 {
905
- font-size: 1.4rem !important;
906
- font-weight: 800 !important;
907
- }
908
-
909
- h2 {
910
- font-size: 1.2rem !important;
911
- font-weight: 600 !important;
912
- }
913
-
914
- h3 {
915
- font-size: 1rem !important;
916
- font-weight: 600 !important;
917
- }
918
-
919
- /* Kart arka plan renkleri - görseldeki gibi */
920
- /* Kullanıcı mesajları için mavi tonda arka plan */
921
- .user-message, .user-message-highlighted {
922
- background-color: #e9f5fe !important;
923
- border-bottom-right-radius: 0 !important;
924
- box-shadow: 0 1px 2px rgba(0,0,0,0.1) !important;
925
- }
926
-
927
- /* Bot mesajları için beyaz arka plan ve hafif kenarlık */
928
- .bot-message, .bot-message-highlighted {
929
- background-color: white !important;
930
- border: 1px solid #e0e0e0 !important;
931
- border-bottom-left-radius: 0 !important;
932
- box-shadow: 0 1px 2px rgba(0,0,0,0.05) !important;
933
- }
934
-
935
- /* Mesaj baloncuklarının köşe yuvarlatma değerleri */
936
- .message-wrap {
937
- border-radius: 12px !important;
938
- margin: 0.5rem 0 !important;
939
- max-width: 90% !important;
940
- }
941
-
942
- /* Sohbet alanının genel arka planı */
943
- .chat-container, .gradio-container {
944
- background-color: #f7f7f7 !important;
945
- }
946
-
947
- /* Daha net yazılar için text rendering */
948
- * {
949
- -webkit-font-smoothing: antialiased;
950
- -moz-osx-font-smoothing: grayscale;
951
- text-rendering: optimizeLegibility;
952
- }
953
-
954
- /* Restore butonu stilleri kaldırıldı */
955
-
956
- /* Responsive mobil tasarım - iOS benzeri */
957
- @media (max-width: 768px) {
958
- /* Daha büyük ve dokunmatik dostu boyutlar */
959
- .gradio-container {
960
- padding: 0 !important;
961
- margin: 0 !important;
962
- }
963
-
964
- /* Mesaj baloncukları iOS tarzı */
965
- .message-wrap {
966
- margin: 0.3rem 0.5rem !important;
967
- max-width: 85% !important;
968
- border-radius: 18px !important;
969
- padding: 10px 15px !important;
970
- font-size: 0.9rem !important;
971
- }
972
-
973
- /* Kullanıcı mesajları */
974
- .user-message, .user-message-highlighted {
975
- background-color: #007AFF !important;
976
- color: white !important;
977
- margin-left: auto !important;
978
- margin-right: 8px !important;
979
- }
980
-
981
- /* Bot mesajları */
982
- .bot-message, .bot-message-highlighted {
983
- background-color: #f1f1f1 !important;
984
- color: #333 !important;
985
- margin-left: 8px !important;
986
- margin-right: auto !important;
987
- }
988
- }
989
-
990
- /* Input alanına uçan kağıt ikonu ekle */
991
- #msg-input {
992
- position: relative;
993
- }
994
-
995
- #msg-input textarea {
996
- padding-right: 40px !important;
997
- }
998
-
999
- .input-icon {
1000
- position: absolute;
1001
- right: 12px;
1002
- top: 50%;
1003
- transform: translateY(-50%);
1004
- font-size: 16px;
1005
- pointer-events: none;
1006
- z-index: 10;
1007
- color: #666;
1008
- }
1009
-
1010
- /* Mobil responsive - Input alanı */
1011
- @media (max-width: 768px) {
1012
- #msg-input {
1013
- margin: 10px 0;
1014
- }
1015
-
1016
- #msg-input textarea {
1017
- padding: 12px 45px 12px 15px !important;
1018
- font-size: 1rem !important;
1019
- border-radius: 20px !important;
1020
- border: 1px solid #ddd !important;
1021
- box-shadow: 0 1px 3px rgba(0,0,0,0.1) !important;
1022
- }
1023
-
1024
- .input-icon {
1025
- right: 15px;
1026
- font-size: 18px;
1027
- }
1028
- }
1029
-
1030
- /* Genel mobil iyileştirmeler */
1031
- @media (max-width: 768px) {
1032
- /* Chatbot alanı tam ekran */
1033
- .gradio-container .main {
1034
- padding: 0 !important;
1035
- }
1036
-
1037
- /* Başlık alanı küçült */
1038
- .gradio-container header {
1039
- padding: 8px !important;
1040
- }
1041
-
1042
- /* Tab alanlarını küçült */
1043
- .tab-nav {
1044
- padding: 5px !important;
1045
- }
1046
-
1047
- /* Scroll bar'ı gizle */
1048
- .scroll-hide {
1049
- scrollbar-width: none;
1050
- -ms-overflow-style: none;
1051
- }
1052
-
1053
- .scroll-hide::-webkit-scrollbar {
1054
- display: none;
1055
- }
1056
- }
1057
-
1058
- /* CSS Bitişi */
1059
  """
1060
 
1061
- # Chat persistence sistemi tamamen kaldırıldı
1062
  storage_js = ""
1063
 
1064
- # Enhanced chatbot fonksiyonu image destekli
1065
  def enhanced_chatbot_fn(message, history, image):
1066
  return chatbot_fn(message, history, image)
1067
 
1068
- # Demo arayüzü - Mobil responsive
1069
  with gr.Blocks(css=custom_css, theme="soft", title="Trek Asistanı", head=storage_js) as demo:
1070
  gr.Markdown("# 🚲 Trek Asistanı AI")
1071
  gr.Markdown("**Akıllı özellikler:** Ürün karşılaştırması ve detaylı ürün bilgileri sunuyorum.")
1072
 
1073
- # LocalStorage fonksiyonu kaldırıldı
1074
-
1075
  chatbot = gr.Chatbot(height=600, elem_id="chatbot", show_label=False, type="messages")
1076
 
1077
  msg = gr.Textbox(
@@ -1080,53 +616,38 @@ with gr.Blocks(css=custom_css, theme="soft", title="Trek Asistanı", head=storag
1080
  elem_id="msg-input"
1081
  )
1082
 
1083
-
1084
- # Session ve profil sistemi tamamen kaldırıldı
1085
-
1086
  def respond(message, chat_history):
1087
  if not message.strip():
1088
  return "", chat_history
1089
-
1090
- # Kullanıcı mesajını hemen göster
1091
  if chat_history is None:
1092
  chat_history = []
1093
-
1094
- # Messages format için kullanıcı mesajını ekle
1095
  chat_history.append({"role": "user", "content": message})
1096
  yield "", chat_history
1097
 
1098
- # Chat history'yi chatbot_fn için uygun formata çevir
1099
  formatted_history = []
1100
- for msg in chat_history[:-1]: # Son mesajı hariç tut
1101
- formatted_history.append(msg)
1102
 
1103
  try:
1104
- # Enhanced chatbot fonksiyonunu çağır (image=None)
1105
  response_generator = chatbot_fn(message, formatted_history, None)
1106
-
1107
- # Generator'dan streaming cevap al
1108
  response = ""
1109
  for partial in response_generator:
1110
  response = partial
1111
- # Bot cevabını ekle veya güncelle
1112
- # Eğer son mesaj user ise, bot mesajı ekle
1113
  if chat_history[-1]["role"] == "user":
1114
  chat_history.append({"role": "assistant", "content": response})
1115
  else:
1116
- # Son bot mesajını güncelle
1117
  chat_history[-1]["content"] = response
1118
  yield "", chat_history
1119
 
1120
- # Konuşmayı kaydet
1121
  try:
1122
  add_conversation(message, response)
 
1123
  except Exception as e:
1124
  print(f"Error saving conversation: {e}")
1125
 
1126
  except Exception as e:
1127
  error_msg = f"Üzgünüm, bir hata oluştu: {str(e)}"
1128
  print(f"Chat error: {e}")
1129
- # Hata mesajı ekle
1130
  if chat_history[-1]["role"] == "user":
1131
  chat_history.append({"role": "assistant", "content": error_msg})
1132
  else:
@@ -1135,7 +656,6 @@ with gr.Blocks(css=custom_css, theme="soft", title="Trek Asistanı", head=storag
1135
 
1136
  msg.submit(respond, [msg, chatbot], [msg, chatbot], show_progress=True)
1137
 
1138
- # Add conversation viewer
1139
  with gr.Accordion("📊 Konuşma Geçmişi (JSON)", open=False):
1140
  with gr.Row():
1141
  refresh_json_btn = gr.Button("🔄 Yenile", scale=1)
@@ -1148,14 +668,13 @@ with gr.Blocks(css=custom_css, theme="soft", title="Trek Asistanı", head=storag
1148
  def get_conversations_json():
1149
  from conversation_tracker import load_conversations
1150
  convs = load_conversations()
1151
- # Also save to file for download
1152
  import json as json_module
1153
  with open("temp_conversations.json", "w", encoding="utf-8") as f:
1154
  json_module.dump(convs, f, ensure_ascii=False, indent=2)
1155
  return convs
1156
 
1157
  def download_conversations():
1158
- get_conversations_json() # Ensure file is updated
1159
  return gr.update(visible=True, value="temp_conversations.json")
1160
 
1161
  def open_dashboard():
@@ -1163,111 +682,56 @@ with gr.Blocks(css=custom_css, theme="soft", title="Trek Asistanı", head=storag
1163
  <div style='padding: 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 10px; color: white;'>
1164
  <h3>📊 Dashboard Kullanım Talimatları</h3>
1165
  <ol style='margin: 15px 0;'>
1166
- <li>Yukarıdaki "💾 JSON İndir" butonuna tıkla</li>
1167
- <li>conversations.json dosyasını indir</li>
1168
- <li>bf_dashboard_json.html dosyasını aç</li>
1169
- <li>İndirdiğin JSON dosyasını dashboard'a yükle</li>
1170
  </ol>
1171
- <p style='margin-top: 15px; font-size: 14px;'>
1172
- Dashboard HTML dosyasını almak için:<br>
1173
- <a href='https://github.com/yourusername/bf-dashboard' target='_blank' style='color: white; text-decoration: underline;'>
1174
- GitHub'dan indir
1175
- </a>
1176
- </p>
1177
  </div>
1178
  """)
1179
 
1180
  refresh_json_btn.click(get_conversations_json, outputs=json_display)
1181
- download_json_btn.click(download_conversations, outputs=download_file)
 
1182
  view_dashboard_btn.click(open_dashboard, outputs=json_display)
1183
-
1184
- # Auto-load on start
1185
  demo.load(get_conversations_json, outputs=json_display)
1186
-
1187
- # Define function for JSON HTML before using it
1188
- def create_json_html():
1189
- """Create HTML with embedded JSON data"""
1190
- from conversation_tracker import load_conversations
1191
- import json
1192
- conversations = load_conversations()
1193
- json_str = json.dumps(conversations, ensure_ascii=False)
1194
- return f'''
1195
- <div id="json-data" style="display:none;">
1196
- <script type="application/json" id="conversations-json">
1197
- {json_str}
1198
- </script>
1199
- </div>
1200
- '''
1201
-
1202
- # Add HTML component with embedded JSON for external access
1203
- with gr.Row(visible=False):
1204
- # Create an HTML element that contains the JSON data
1205
- json_html = gr.HTML(
1206
- value=create_json_html(), # Call directly, not as lambda
1207
- visible=False,
1208
- elem_id="json_data_container"
1209
- )
1210
-
1211
- # Update JSON HTML on page load
1212
- demo.load(create_json_html, outputs=json_html)
1213
-
1214
- # API endpoints for dashboard
1215
- def get_all_conversations():
1216
- """API endpoint to get all conversations"""
1217
- from conversation_tracker import load_conversations
1218
- conversations = load_conversations()
1219
-
1220
- # Format for dashboard
1221
- formatted = {}
1222
- for conv in conversations:
1223
- session_id = f"session_{conv['timestamp'].replace(':', '').replace('-', '').replace('T', '_')[:15]}"
1224
-
1225
- if session_id not in formatted:
1226
- formatted[session_id] = {
1227
- "customer": "Kullanıcı",
1228
- "phone": session_id,
1229
- "messages": []
1230
- }
1231
-
1232
- formatted[session_id]["messages"].append({
1233
- "type": "received",
1234
- "text": conv["user"],
1235
- "time": conv["timestamp"]
1236
- })
1237
-
1238
- formatted[session_id]["messages"].append({
1239
- "type": "sent",
1240
- "text": conv["bot"],
1241
- "time": conv["timestamp"]
1242
- })
1243
-
1244
- return formatted
1245
-
1246
- def get_conversation_stats():
1247
- """Get conversation statistics"""
1248
- from conversation_tracker import load_conversations
1249
- from datetime import datetime
1250
-
1251
- conversations = load_conversations()
1252
- today = datetime.now().date()
1253
- today_count = sum(1 for conv in conversations
1254
- if datetime.fromisoformat(conv["timestamp"]).date() == today)
1255
-
1256
- return {
1257
- "total": len(conversations),
1258
- "today": today_count,
1259
- "active": len(set(conv.get("session_id", i) for i, conv in enumerate(conversations)))
1260
- }
1261
 
1262
- # API will be handled separately in production
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1263
 
1264
- # Make conversations.json accessible via Gradio file serving
1265
- # The file will be accessible at: https://samikoen-bf.hf.space/file=conversations.json
1266
- # But we need to ensure it's served with proper content type
1267
 
1268
  if __name__ == "__main__":
1269
- # Launch with allowed paths for file serving
1270
- demo.launch(
1271
- debug=True,
1272
- allowed_paths=["conversations.json", "public/conversations.json"]
1273
- )
 
15
  from googleapiclient.discovery import build
16
  from googleapiclient.http import MediaIoBaseDownload
17
  import io
 
18
  from requests.packages.urllib3.exceptions import InsecureRequestWarning
19
  warnings.simplefilter('ignore', InsecureRequestWarning)
20
 
 
31
  # Import conversation tracker
32
  from conversation_tracker import add_conversation
33
 
34
+ # === JSON dashboard için yardımcılar ===
35
+ def save_conversations_json():
36
+ """
37
+ conversation_tracker.load_conversations() çıktısını
38
+ hem köke hem public/ altına yazar. Dashboard bunları aynı origin'den çeker
39
+ ya da standalone HTML CORS açık /api uçlarından alır.
40
+ """
41
+ try:
42
+ from conversation_tracker import load_conversations
43
+ convs = load_conversations()
44
+ # kök
45
+ with open("conversations.json", "w", encoding="utf-8") as f:
46
+ json.dump(convs, f, ensure_ascii=False, indent=2)
47
+ # public/
48
+ os.makedirs("public", exist_ok=True)
49
+ with open("public/conversations.json", "w", encoding="utf-8") as f:
50
+ json.dump(convs, f, ensure_ascii=False, indent=2)
51
+ print("conversations.json ve public/conversations.json güncellendi.")
52
+ except Exception as e:
53
+ print(f"conversations.json yazma hatası: {e}")
54
 
55
  # Import smart warehouse with GPT intelligence and price
56
  try:
 
78
  """Smart warehouse stock finder with general algorithm"""
79
  try:
80
  import re
 
81
 
82
  # Get XML with retry
83
  xml_text = None
 
115
 
116
  # If query is very short (like "hangi boyu"), skip it
117
  if len(words) <= 2 and not any(w.isdigit() for w in words):
 
118
  pass
119
  else:
 
120
  for word in words:
 
121
  if word in sizes:
122
  continue
 
 
123
  if word.isdigit():
124
  product_words.append(word)
 
 
125
  elif any(c.isdigit() for c in word) and any(c.isalpha() for c in word):
126
  product_words.append(word)
 
 
127
  elif len(word) in [2, 3] and word.isalpha():
 
128
  if word in ['mi', 'mı', 'mu', 'mü', 'var', 'yok', 've', 'de', 'da']:
129
  continue
 
130
  if any(c not in 'aeiouı' for c in word):
131
  product_words.append(word)
 
 
132
  elif len(word) > 3:
 
133
  if any(word.endswith(suffix) for suffix in ['mi', 'mı', 'mu', 'mü']):
134
  continue
 
135
  consonants = sum(1 for c in word if c not in 'aeiouı')
136
+ if consonants <= 2:
137
  continue
 
138
  product_words.append(word)
139
 
140
  print(f"DEBUG - Searching: {' '.join(product_words)}, Size: {size}")
 
145
 
146
  print(f"DEBUG - Total products in XML: {len(all_products)}")
147
 
 
148
  best_match = None
 
149
  for product_block in all_products:
 
150
  name_match = re.search(r'<ProductName><!\[CDATA\[(.*?)\]\]></ProductName>', product_block)
151
  if not name_match:
152
  continue
 
154
  product_name_in_xml = name_match.group(1)
155
  normalized_xml_name = normalize(product_name_in_xml)
156
 
 
 
157
  match = True
158
  for word in product_words:
 
159
  if word not in normalized_xml_name:
 
160
  if not (word.isdigit() and any(f"{prev}{word}" in normalized_xml_name or f"{prev} {word}" in normalized_xml_name for prev in product_words if not prev.isdigit())):
161
  match = False
162
  break
163
 
164
  if match:
 
 
165
  if size:
 
166
  variant_match = re.search(r'<ProductVariant><!\[CDATA\[(.*?)\]\]></ProductVariant>', product_block)
167
  if variant_match:
168
  variant = variant_match.group(1)
 
169
  if variant.upper().startswith(f'{size.upper()}-'):
170
  print(f"DEBUG - Found match: {product_name_in_xml} - {variant}")
171
  best_match = product_block
172
+ break
173
  else:
 
174
  best_match = product_block
175
  break
176
 
177
  if best_match:
 
178
  warehouse_info = []
179
  warehouse_regex = r'<Warehouse>.*?<Name><!\[CDATA\[(.*?)\]\]></Name>.*?<Stock>(.*?)</Stock>.*?</Warehouse>'
180
  warehouses = re.findall(warehouse_regex, best_match, re.DOTALL)
 
183
  try:
184
  stock = int(wh_stock.strip())
185
  if stock > 0:
 
186
  if "CADDEBOSTAN" in wh_name:
187
  display = "Caddebostan mağazası"
188
  elif "ORTAKÖY" in wh_name:
 
193
  display = "Bahçeköy mağazası"
194
  else:
195
  display = wh_name
 
196
  warehouse_info.append(f"{display}: Mevcut")
197
  except:
198
  pass
 
206
  print(f"Warehouse error: {e}")
207
  return None
208
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
209
  # Import improved product search
210
  try:
211
  from improved_chatbot import ImprovedChatbot
 
240
  products = []
241
  if root is not None:
242
  for item in root.findall('item'):
 
243
  rootlabel_elem = item.find('rootlabel')
244
  stock_elem = item.find('stockAmount')
 
245
  if rootlabel_elem is None or stock_elem is None:
246
+ continue
247
 
248
  name_words = rootlabel_elem.text.lower().split()
249
  name = name_words[0]
 
251
 
252
  stock_amount = "stokta" if stock_elem.text and stock_elem.text > '0' else "stokta değil"
253
 
 
254
  if stock_amount == "stokta":
 
255
  price_elem = item.find('priceTaxWithCur')
256
  price_str = price_elem.text if price_elem is not None and price_elem.text else "Fiyat bilgisi yok"
 
 
257
  price_eft_elem = item.find('priceEft')
258
  price_eft_str = price_eft_elem.text if price_eft_elem is not None and price_eft_elem.text else ""
 
 
259
  price_rebate_elem = item.find('priceRebateWithTax')
260
  price_rebate_str = price_rebate_elem.text if price_rebate_elem is not None and price_rebate_elem.text else ""
 
 
261
  price_rebate_money_order_elem = item.find('priceRebateWithMoneyOrderWithTax')
262
  price_rebate_money_order_str = price_rebate_money_order_elem.text if price_rebate_money_order_elem is not None and price_rebate_money_order_elem.text else ""
263
 
 
264
  try:
265
  price_float = float(price_str)
 
266
  if price_float > 200000:
267
+ price = str(round(price_float / 5000) * 5000)
 
268
  elif price_float > 30000:
269
+ price = str(round(price_float / 1000) * 1000)
 
270
  elif price_float > 10000:
271
+ price = str(round(price_float / 100) * 100)
 
272
  else:
273
+ price = str(round(price_float / 10) * 10)
274
  except (ValueError, TypeError):
275
+ price = price_str
276
 
 
277
  if price_eft_str:
278
  try:
279
  price_eft_float = float(price_eft_str)
 
280
  if price_eft_float > 200000:
281
  price_eft = str(round(price_eft_float / 5000) * 5000)
 
282
  elif price_eft_float > 30000:
283
  price_eft = str(round(price_eft_float / 1000) * 1000)
 
284
  elif price_eft_float > 10000:
285
  price_eft = str(round(price_eft_float / 100) * 100)
 
286
  else:
287
  price_eft = str(round(price_eft_float / 10) * 10)
288
  except (ValueError, TypeError):
 
290
  else:
291
  price_eft = ""
292
 
 
293
  if price_rebate_str:
294
  try:
295
  price_rebate_float = float(price_rebate_str)
 
296
  if price_rebate_float > 200000:
297
  price_rebate = str(round(price_rebate_float / 5000) * 5000)
 
298
  elif price_rebate_float > 30000:
299
  price_rebate = str(round(price_rebate_float / 1000) * 1000)
 
300
  elif price_rebate_float > 10000:
301
  price_rebate = str(round(price_rebate_float / 100) * 100)
 
302
  else:
303
  price_rebate = str(round(price_rebate_float / 10) * 10)
304
  except (ValueError, TypeError):
 
306
  else:
307
  price_rebate = ""
308
 
 
309
  if price_rebate_money_order_str:
310
  try:
311
  price_rebate_money_order_float = float(price_rebate_money_order_str)
 
312
  if price_rebate_money_order_float > 200000:
313
  price_rebate_money_order = str(round(price_rebate_money_order_float / 5000) * 5000)
 
314
  elif price_rebate_money_order_float > 30000:
315
  price_rebate_money_order = str(round(price_rebate_money_order_float / 1000) * 1000)
 
316
  elif price_rebate_money_order_float > 10000:
317
  price_rebate_money_order = str(round(price_rebate_money_order_float / 100) * 100)
 
318
  else:
319
  price_rebate_money_order = str(round(price_rebate_money_order_float / 10) * 10)
320
  except (ValueError, TypeError):
 
322
  else:
323
  price_rebate_money_order = ""
324
 
 
325
  product_url_elem = item.find('productLink')
326
  product_url = product_url_elem.text if product_url_elem is not None and product_url_elem.text else ""
327
  product_info = [stock_amount, price, product_url, price_eft, price_rebate, price_rebate_money_order]
328
 
 
329
  image_elem = item.find('picture1Path')
330
  if image_elem is not None and image_elem.text:
331
  product_info.append(image_elem.text)
332
  else:
333
+ product_info.append("")
 
334
  else:
 
335
  product_info = [stock_amount]
336
 
337
  products.append((name, product_info, full_name))
 
341
  # Initialize enhanced features
342
  initialize_enhanced_features(OPENAI_API_KEY, products)
343
 
344
+ # İlk başta dosyaları oluştur (boşsa)
345
+ save_conversations_json()
346
+
347
  # Initialize improved chatbot if available
348
  improved_bot = None
349
  if USE_IMPROVED_SEARCH:
 
376
  credentials = Credentials.from_service_account_file(GOOGLE_CREDENTIALS_PATH)
377
  service = build('drive', 'v3', credentials=credentials)
378
 
 
379
  results = service.files().list(
380
  q=f"'{GOOGLE_FOLDER_ID}' in parents",
381
  fields="files(id, name, mimeType)"
 
386
 
387
  for file in files:
388
  print(f"İndiriliyor: {file['name']}")
 
 
389
  if file['mimeType'] == 'application/vnd.openxmlformats-officedocument.wordprocessingml.document':
390
  request = service.files().get_media(fileId=file['id'])
391
  file_io = io.BytesIO()
 
398
  file_io.seek(0)
399
  doc = Document(file_io)
400
 
401
+ content = f"\n=== {file['name']} ===\n"
402
  for paragraph in doc.paragraphs:
403
  if paragraph.text.strip():
404
+ content += paragraph.text + "\n"
405
 
406
  all_content.append(content)
407
 
408
+ document_content = "\n".join(all_content)
409
  print(f"Toplam {len(files)} döküman yüklendi.")
410
 
411
  except Exception as e:
 
421
  if os.path.exists(LOG_FILE):
422
  with file_lock:
423
  with open(LOG_FILE, 'w', encoding='utf-8') as f:
424
+ f.write("Log dosyası temizlendi.\n")
425
  print("Log dosyası temizlendi.")
426
  except Exception as e:
427
  print(f"Log dosyası temizleme hatası: {e}")
 
429
  # Zamanlanmış görevleri çalıştırma fonksiyonu
430
  def run_scheduler(chat_history):
431
  schedule.every().day.at("03:00").do(clear_log_file)
 
432
  while True:
433
  schedule.run_pending()
434
  time.sleep(60)
 
438
  if history is None:
439
  history = []
440
 
 
441
  warehouse_stock_data = None
442
  print(f"DEBUG - Getting warehouse stock FIRST for: {user_message}")
443
  try:
444
  warehouse_stock_data = get_warehouse_stock(user_message)
445
  if warehouse_stock_data:
446
+ print(f"DEBUG - Warehouse stock found: {warehouse_stock_data[:2]}...")
447
  else:
448
  print(f"DEBUG - No warehouse stock data returned")
449
  except Exception as e:
450
  print(f"DEBUG - Warehouse stock error at start: {e}")
451
 
452
  try:
 
453
  if image is not None:
454
  user_message = process_image_message(image, user_message)
455
 
 
456
  comparison_result = handle_comparison_request(user_message)
457
  if comparison_result:
458
  yield comparison_result
459
  return
 
 
 
 
460
  except Exception as e:
461
  print(f"Enhanced features error: {e}")
 
462
 
 
463
  try:
464
  with file_lock:
465
  with open(LOG_FILE, 'a', encoding='utf-8') as f:
466
+ f.write(f"User: {user_message}\n")
467
  except Exception as e:
468
  print(f"Dosya yazma hatası (Kullanıcı): {e}")
469
 
 
470
  system_messages = get_prompt_content_only()
471
 
 
472
  if document_content:
473
  system_messages.append({"role": "system", "content": f"Dökümanlardan gelen bilgiler: {document_content}"})
474
 
 
 
 
475
  product_found_improved = False
476
  if USE_IMPROVED_SEARCH and improved_bot:
477
  try:
478
  product_result = improved_bot.process_message(user_message)
479
  if product_result['is_product_query'] and product_result['response']:
 
480
  enhanced_response = product_result['response']
 
 
481
  if warehouse_stock_data and warehouse_stock_data != ["Hiçbir mağazada mevcut değil"]:
482
  warehouse_info = f"\n\n🏪 MAĞAZA STOK BİLGİLERİ:\n"
483
  for store_info in warehouse_stock_data:
 
496
  except Exception as e:
497
  print(f"Improved search error: {e}")
498
 
 
499
  if not product_found_improved:
500
  print(f"DEBUG chatbot_fn - Using warehouse stock data for: {user_message}")
 
 
501
  if warehouse_stock_data and warehouse_stock_data != ["Hiçbir mağazada mevcut değil"]:
502
  warehouse_info = f"🏪 MAĞAZA STOK BİLGİLERİ:\n"
503
  for store_info in warehouse_stock_data:
 
519
  messages = system_messages + history + [{"role": "user", "content": user_message}]
520
 
521
  payload = {
522
+ "model": "gpt-4.1",
523
  "messages": messages,
524
  "temperature": 0.2,
525
  "top_p": 1,
 
550
  delta = chunk_data['choices'][0]['delta']
551
  if 'content' in delta:
552
  partial_response += delta['content']
 
553
  formatted_response = extract_product_info_for_gallery(partial_response)
554
  yield formatted_response
555
  except json.JSONDecodeError as e:
 
557
  elif chunk_str == "data: [DONE]":
558
  break
559
 
 
560
  final_response = extract_product_info_for_gallery(partial_response)
561
  yield final_response
562
 
 
 
 
563
  try:
564
  with file_lock:
565
  with open(LOG_FILE, 'a', encoding='utf-8') as f:
566
+ f.write(f"Bot: {partial_response}\n")
567
  except Exception as e:
568
  print(f"Dosya yazma hatası (Bot): {e}")
569
 
 
570
  with history_lock:
571
  global_chat_history.append({"role": "user", "content": user_message})
572
  global_chat_history.append({"role": "assistant", "content": partial_response})
 
585
  scheduler_thread = threading.Thread(target=run_scheduler, args=(global_chat_history,), daemon=True)
586
  scheduler_thread.start()
587
 
588
+ # Trek markasına özel tema (kısaltılmış)
589
  trek_theme = gr.themes.Base(
590
+ primary_hue="red",
591
+ secondary_hue="slate",
592
  neutral_hue="slate",
593
+ radius_size=gr.themes.sizes.radius_sm,
594
+ spacing_size=gr.themes.sizes.spacing_md,
595
+ text_size=gr.themes.sizes.text_sm
596
  )
 
597
  custom_css = """
598
+ /* temel stiller (kısaltılmış) */
599
+ .gradio-container, .gradio-container * { font-family: 'Segoe UI', 'SF Pro Text', 'Roboto', -apple-system, BlinkMacSystemFont, 'Helvetica Neue', Arial, sans-serif !important; font-size: 0.6rem !important; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
600
  """
601
 
 
602
  storage_js = ""
603
 
 
604
  def enhanced_chatbot_fn(message, history, image):
605
  return chatbot_fn(message, history, image)
606
 
 
607
  with gr.Blocks(css=custom_css, theme="soft", title="Trek Asistanı", head=storage_js) as demo:
608
  gr.Markdown("# 🚲 Trek Asistanı AI")
609
  gr.Markdown("**Akıllı özellikler:** Ürün karşılaştırması ve detaylı ürün bilgileri sunuyorum.")
610
 
 
 
611
  chatbot = gr.Chatbot(height=600, elem_id="chatbot", show_label=False, type="messages")
612
 
613
  msg = gr.Textbox(
 
616
  elem_id="msg-input"
617
  )
618
 
 
 
 
619
  def respond(message, chat_history):
620
  if not message.strip():
621
  return "", chat_history
 
 
622
  if chat_history is None:
623
  chat_history = []
 
 
624
  chat_history.append({"role": "user", "content": message})
625
  yield "", chat_history
626
 
 
627
  formatted_history = []
628
+ for m in chat_history[:-1]:
629
+ formatted_history.append(m)
630
 
631
  try:
 
632
  response_generator = chatbot_fn(message, formatted_history, None)
 
 
633
  response = ""
634
  for partial in response_generator:
635
  response = partial
 
 
636
  if chat_history[-1]["role"] == "user":
637
  chat_history.append({"role": "assistant", "content": response})
638
  else:
 
639
  chat_history[-1]["content"] = response
640
  yield "", chat_history
641
 
 
642
  try:
643
  add_conversation(message, response)
644
+ save_conversations_json() # JSON’u Space kökü ve public/ altına yaz
645
  except Exception as e:
646
  print(f"Error saving conversation: {e}")
647
 
648
  except Exception as e:
649
  error_msg = f"Üzgünüm, bir hata oluştu: {str(e)}"
650
  print(f"Chat error: {e}")
 
651
  if chat_history[-1]["role"] == "user":
652
  chat_history.append({"role": "assistant", "content": error_msg})
653
  else:
 
656
 
657
  msg.submit(respond, [msg, chatbot], [msg, chatbot], show_progress=True)
658
 
 
659
  with gr.Accordion("📊 Konuşma Geçmişi (JSON)", open=False):
660
  with gr.Row():
661
  refresh_json_btn = gr.Button("🔄 Yenile", scale=1)
 
668
  def get_conversations_json():
669
  from conversation_tracker import load_conversations
670
  convs = load_conversations()
 
671
  import json as json_module
672
  with open("temp_conversations.json", "w", encoding="utf-8") as f:
673
  json_module.dump(convs, f, ensure_ascii=False, indent=2)
674
  return convs
675
 
676
  def download_conversations():
677
+ get_conversations_json()
678
  return gr.update(visible=True, value="temp_conversations.json")
679
 
680
  def open_dashboard():
 
682
  <div style='padding: 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 10px; color: white;'>
683
  <h3>📊 Dashboard Kullanım Talimatları</h3>
684
  <ol style='margin: 15px 0;'>
685
+ <li>Standalone HTML'i (bf_dashboard_auto.html) bilgisayarında aç</li>
686
+ <li>HTML, Space'teki CORS açık API'den JSON'u otomatik çekecek</li>
 
 
687
  </ol>
 
 
 
 
 
 
688
  </div>
689
  """)
690
 
691
  refresh_json_btn.click(get_conversations_json, outputs=json_display)
692
+ download_conversations_btn = download_json_btn
693
+ download_conversations_btn.click(download_conversations, outputs=download_file)
694
  view_dashboard_btn.click(open_dashboard, outputs=json_display)
 
 
695
  demo.load(get_conversations_json, outputs=json_display)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
696
 
697
+ # -------------------------
698
+ # CORS açık API + Gradio mount
699
+ # -------------------------
700
+ from fastapi import FastAPI, Response
701
+ from fastapi.middleware.cors import CORSMiddleware
702
+ import uvicorn
703
+
704
+ api = FastAPI()
705
+ api.add_middleware(
706
+ CORSMiddleware,
707
+ allow_origins=["*"], # Standalone HTML için tüm originlere izin
708
+ allow_credentials=False,
709
+ allow_methods=["GET"],
710
+ allow_headers=["*"],
711
+ )
712
+
713
+ def _read_json_safely(path):
714
+ try:
715
+ with open(path, "r", encoding="utf-8") as f:
716
+ return f.read(), 200
717
+ except FileNotFoundError:
718
+ return json.dumps([]), 200
719
+ except Exception as e:
720
+ return json.dumps({"error": str(e)}), 500
721
+
722
+ @api.get("/api/conversations")
723
+ def api_conversations():
724
+ body, code = _read_json_safely("conversations.json")
725
+ return Response(content=body, media_type="application/json", status_code=code)
726
+
727
+ @api.get("/api/public/conversations")
728
+ def api_public_conversations():
729
+ body, code = _read_json_safely("public/conversations.json")
730
+ return Response(content=body, media_type="application/json", status_code=code)
731
 
732
+ # Gradio arayüzünü FastAPI altına mount et
733
+ app = gr.mount_gradio_app(api, demo, path="/")
 
734
 
735
  if __name__ == "__main__":
736
+ # Space ortamında da çalışır
737
+ uvicorn.run(app, host="0.0.0.0", port=int(os.getenv("PORT", "7860")))