SamiKoen commited on
Commit
c0e544a
·
verified ·
1 Parent(s): 0c7b9fd

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +937 -225
app.py CHANGED
@@ -6,80 +6,68 @@ import xml.etree.ElementTree as ET
6
  import schedule
7
  import time
8
  import threading
 
9
  import warnings
 
10
  from docx import Document
 
11
  from google.oauth2.service_account import Credentials
12
  from googleapiclient.discovery import build
13
  from googleapiclient.http import MediaIoBaseDownload
14
  import io
 
15
  from requests.packages.urllib3.exceptions import InsecureRequestWarning
16
- from fastapi import FastAPI, Response
17
- from fastapi.middleware.cors import CORSMiddleware
18
  warnings.simplefilter('ignore', InsecureRequestWarning)
 
 
19
  from prompts import get_prompt_content_only
20
- from enhanced_features import initialize_enhanced_features, process_image_message, handle_comparison_request
 
 
 
 
 
21
  from image_renderer import extract_product_info_for_gallery, format_message_with_images
 
 
22
  from conversation_tracker import add_conversation
23
 
24
- def save_conversations_json():
25
- try:
26
- from conversation_tracker import load_conversations
27
- convs = load_conversations()
28
- with open("conversations.json", "w", encoding="utf-8") as f:
29
- json.dump(convs, f, ensure_ascii=False, indent=2)
30
- os.makedirs("public", exist_ok=True)
31
- with open("public/conversations.json", "w", encoding="utf-8") as f:
32
- json.dump(convs, f, ensure_ascii=False, indent=2)
33
- except Exception as e:
34
- print(f"conversations.json yazma hatası: {e}")
35
 
 
36
  try:
37
- from improved_chatbot import ImprovedChatbot
38
- USE_IMPROVED_SEARCH = True
39
- except ImportError as e:
40
- print(f"DEBUG - Improved chatbot not available: {e}, using basic search")
41
- USE_IMPROVED_SEARCH = False
42
-
43
- LOG_FILE = '/data/chat_logs.txt' if os.path.exists('/data') else 'chat_logs.txt'
44
- print(f"Dosya yolu: {os.path.abspath(LOG_FILE)}")
45
- API_URL = "https://api.openai.com/v1/chat/completions"
46
- OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
47
- if not OPENAI_API_KEY:
48
- print("Hata: OPENAI_API_KEY çevre değişkeni ayarlanmamış!")
49
-
50
- GOOGLE_CREDENTIALS_PATH = os.getenv("GOOGLE_CREDENTIALS_PATH")
51
- GOOGLE_FOLDER_ID = "1bE8aMj8-eFGftjMPOF8bKQJAhfHa0BN8"
52
- file_lock = threading.Lock()
53
- history_lock = threading.Lock()
54
- global_chat_history = []
55
- document_content = ""
56
- products = []
57
- improved_bot = None
58
 
59
  def get_warehouse_stock(product_name):
60
- try:
61
- from smart_warehouse_with_price import get_warehouse_stock_smart_with_price
62
- s = get_warehouse_stock_smart_with_price(product_name)
63
- if s:
64
- return s
65
- except Exception:
66
- try:
67
- from smart_warehouse import get_warehouse_stock_smart
68
- s = get_warehouse_stock_smart(product_name)
69
- if s:
70
- return s
71
- except Exception:
72
- pass
73
  return get_warehouse_stock_old(product_name)
74
 
 
75
  def get_warehouse_stock_old(product_name):
 
76
  try:
77
  import re
 
 
 
78
  xml_text = None
79
  for attempt in range(3):
80
  try:
81
  url = 'https://video.trek-turkey.com/bizimhesap-warehouse-xml-b2b-api-v2.php'
82
- timeout_val = 10 + (attempt * 5)
83
  response = requests.get(url, verify=False, timeout=timeout_val)
84
  xml_text = response.text
85
  break
@@ -88,70 +76,126 @@ def get_warehouse_stock_old(product_name):
88
  return None
89
  except Exception:
90
  return None
 
 
91
  def normalize(text):
92
  tr_map = {'ı': 'i', 'ğ': 'g', 'ü': 'u', 'ş': 's', 'ö': 'o', 'ç': 'c', 'İ': 'i', 'I': 'i'}
93
  text = text.lower()
94
  for tr, en in tr_map.items():
95
  text = text.replace(tr, en)
96
  return text
 
 
97
  query = normalize(product_name.strip()).replace('(2026)', '').replace('(2025)', '').strip()
98
  words = query.split()
 
 
99
  sizes = ['s', 'm', 'l', 'xl', 'xs', 'xxl', 'ml']
100
  size = next((w for w in words if w in sizes), None)
 
 
101
  product_words = []
102
- if len(words) > 2 or any(w.isdigit() for w in words):
 
 
 
 
 
 
103
  for word in words:
 
104
  if word in sizes:
105
  continue
 
 
106
  if word.isdigit():
107
  product_words.append(word)
 
 
108
  elif any(c.isdigit() for c in word) and any(c.isalpha() for c in word):
109
  product_words.append(word)
 
 
110
  elif len(word) in [2, 3] and word.isalpha():
 
111
  if word in ['mi', 'mı', 'mu', 'mü', 'var', 'yok', 've', 'de', 'da']:
112
  continue
 
113
  if any(c not in 'aeiouı' for c in word):
114
  product_words.append(word)
 
 
115
  elif len(word) > 3:
 
116
  if any(word.endswith(suffix) for suffix in ['mi', 'mı', 'mu', 'mü']):
117
  continue
 
118
  consonants = sum(1 for c in word if c not in 'aeiouı')
119
- if consonants <= 2:
120
  continue
 
121
  product_words.append(word)
122
- import re as _re
123
- all_products = _re.findall(r'<Product>(.*?)</Product>', xml_text, _re.DOTALL)
 
 
 
 
 
 
 
 
124
  best_match = None
 
125
  for product_block in all_products:
126
- name_match = _re.search(r'<ProductName><!\[CDATA\[(.*?)\]\]></ProductName>', product_block)
 
127
  if not name_match:
128
  continue
 
129
  product_name_in_xml = name_match.group(1)
130
  normalized_xml_name = normalize(product_name_in_xml)
 
 
 
131
  match = True
132
  for word in product_words:
 
133
  if word not in normalized_xml_name:
 
134
  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())):
135
  match = False
136
  break
 
137
  if match:
 
 
138
  if size:
139
- variant_match = _re.search(r'<ProductVariant><!\[CDATA\[(.*?)\]\]></ProductVariant>', product_block)
 
140
  if variant_match:
141
  variant = variant_match.group(1)
 
142
  if variant.upper().startswith(f'{size.upper()}-'):
 
143
  best_match = product_block
144
- break
145
  else:
 
146
  best_match = product_block
147
  break
 
148
  if best_match:
 
149
  warehouse_info = []
150
- warehouses = _re.findall(r'<Warehouse>.*?<Name><!\[CDATA\[(.*?)\]\]></Name>.*?<Stock>(.*?)</Stock>.*?</Warehouse>', best_match, _re.DOTALL)
 
 
151
  for wh_name, wh_stock in warehouses:
152
  try:
153
  stock = int(wh_stock.strip())
154
  if stock > 0:
 
155
  if "CADDEBOSTAN" in wh_name:
156
  display = "Caddebostan mağazası"
157
  elif "ORTAKÖY" in wh_name:
@@ -162,216 +206,586 @@ def get_warehouse_stock_old(product_name):
162
  display = "Bahçeköy mağazası"
163
  else:
164
  display = wh_name
 
165
  warehouse_info.append(f"{display}: Mevcut")
166
- except Exception:
167
  pass
 
168
  return warehouse_info if warehouse_info else ["Hiçbir mağazada mevcut değil"]
169
  else:
 
170
  return ["Hiçbir mağazada mevcut değil"]
 
171
  except Exception as e:
172
  print(f"Warehouse error: {e}")
173
  return None
174
 
175
- def load_products():
176
- out = []
 
177
  try:
178
- url = 'https://www.trekbisiklet.com.tr/output/8582384479'
179
- response = requests.get(url, verify=False, timeout=30)
180
- if response.status_code != 200 or not response.content:
181
- return out
182
- root = ET.fromstring(response.content)
183
- for item in root.findall('item'):
184
- rootlabel_elem = item.find('rootlabel')
185
- stock_elem = item.find('stockAmount')
186
- if rootlabel_elem is None or stock_elem is None or rootlabel_elem.text is None:
187
- continue
188
- name_words = rootlabel_elem.text.lower().split()
189
- if not name_words:
190
- continue
191
- name = name_words[0]
192
- full_name = ' '.join(name_words)
193
- stock_amount = "stokta" if stock_elem.text and stock_elem.text > '0' else "stokta değil"
194
- if stock_amount == "stokta":
195
- price_elem = item.find('priceTaxWithCur')
196
- price_str = price_elem.text if price_elem is not None and price_elem.text else "Fiyat bilgisi yok"
197
- price_eft_elem = item.find('priceEft')
198
- price_eft_str = price_eft_elem.text if price_eft_elem is not None and price_eft_elem.text else ""
199
- price_rebate_elem = item.find('priceRebateWithTax')
200
- price_rebate_str = price_rebate_elem.text if price_rebate_elem is not None and price_rebate_elem.text else ""
201
- price_rebate_money_order_elem = item.find('priceRebateWithMoneyOrderWithTax')
202
- 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 ""
203
- try:
204
- price_float = float(price_str)
205
- if price_float > 200000:
206
- price = str(round(price_float / 5000) * 5000)
207
- elif price_float > 30000:
208
- price = str(round(price_float / 1000) * 1000)
209
- elif price_float > 10000:
210
- price = str(round(price_float / 100) * 100)
211
- else:
212
- price = str(round(price_float / 10) * 10)
213
- except Exception:
214
- price = price_str
215
- if price_eft_str:
216
- try:
217
- price_eft_float = float(price_eft_str)
218
- if price_eft_float > 200000:
219
- price_eft = str(round(price_eft_float / 5000) * 5000)
220
- elif price_eft_float > 30000:
221
- price_eft = str(round(price_eft_float / 1000) * 1000)
222
- elif price_eft_float > 10000:
223
- price_eft = str(round(price_eft_float / 100) * 100)
224
- else:
225
- price_eft = str(round(price_eft_float / 10) * 10)
226
- except Exception:
227
- price_eft = price_eft_str
228
- else:
229
- price_eft = ""
230
- if price_rebate_str:
231
- try:
232
- price_rebate_float = float(price_rebate_str)
233
- if price_rebate_float > 200000:
234
- price_rebate = str(round(price_rebate_float / 5000) * 5000)
235
- elif price_rebate_float > 30000:
236
- price_rebate = str(round(price_rebate_float / 1000) * 1000)
237
- elif price_rebate_float > 10000:
238
- price_rebate = str(round(price_rebate_float / 100) * 100)
239
- else:
240
- price_rebate = str(round(price_rebate_float / 10) * 10)
241
- except Exception:
242
- price_rebate = price_rebate_str
243
- else:
244
- price_rebate = ""
245
- if price_rebate_money_order_str:
246
- try:
247
- price_rebate_money_order_float = float(price_rebate_money_order_str)
248
- if price_rebate_money_order_float > 200000:
249
- price_rebate_money_order = str(round(price_rebate_money_order_float / 5000) * 5000)
250
- elif price_rebate_money_order_float > 30000:
251
- price_rebate_money_order = str(round(price_rebate_money_order_float / 1000) * 1000)
252
- elif price_rebate_money_order_float > 10000:
253
- price_rebate_money_order = str(round(price_rebate_money_order_float / 100) * 100)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
254
  else:
255
- price_rebate_money_order = str(round(price_rebate_money_order_float / 10) * 10)
256
- except Exception:
257
- price_rebate_money_order = price_rebate_money_order_str
258
- else:
259
- price_rebate_money_order = ""
260
- product_url_elem = item.find('productLink')
261
- product_url = product_url_elem.text if product_url_elem is not None and product_url_elem.text else ""
262
- product_info = [stock_amount, price, product_url, price_eft, price_rebate, price_rebate_money_order]
263
- image_elem = item.find('picture1Path')
264
- if image_elem is not None and image_elem.text:
265
- product_info.append(image_elem.text)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
266
  else:
267
- product_info.append("")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
268
  else:
269
- product_info = [stock_amount]
270
- out.append((name, product_info, full_name))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
271
  except Exception as e:
272
- print(f"Ürün yükleme hatası: {e}")
273
- return out
 
 
 
 
 
 
 
 
 
 
274
 
 
275
  def download_documents_from_drive():
276
  global document_content
 
277
  if not GOOGLE_CREDENTIALS_PATH:
278
  print("Google credentials dosyası bulunamadı.")
279
  return
 
280
  try:
281
  credentials = Credentials.from_service_account_file(GOOGLE_CREDENTIALS_PATH)
282
  service = build('drive', 'v3', credentials=credentials)
283
- results = service.files().list(q=f"'{GOOGLE_FOLDER_ID}' in parents", fields="files(id, name, mimeType)").execute()
 
 
 
 
 
 
284
  files = results.get('files', [])
285
  all_content = []
 
286
  for file in files:
287
- if file.get('mimeType') == 'application/vnd.openxmlformats-officedocument.wordprocessingml.document':
 
 
 
288
  request = service.files().get_media(fileId=file['id'])
289
  file_io = io.BytesIO()
290
  downloader = MediaIoBaseDownload(file_io, request)
 
291
  done = False
292
  while done is False:
293
  status, done = downloader.next_chunk()
 
294
  file_io.seek(0)
295
  doc = Document(file_io)
296
- content = f"\n=== {file['name']} ===\n"
 
297
  for paragraph in doc.paragraphs:
298
  if paragraph.text.strip():
299
- content += paragraph.text + "\n"
 
300
  all_content.append(content)
301
- document_content = "\n".join(all_content)
 
 
 
302
  except Exception as e:
303
  print(f"Google Drive'dan döküman indirme hatası: {e}")
304
 
 
 
 
 
 
305
  def clear_log_file():
306
  try:
307
  if os.path.exists(LOG_FILE):
308
  with file_lock:
309
  with open(LOG_FILE, 'w', encoding='utf-8') as f:
310
- f.write("Log dosyası temizlendi.\n")
 
311
  except Exception as e:
312
  print(f"Log dosyası temizleme hatası: {e}")
313
 
 
314
  def run_scheduler(chat_history):
315
  schedule.every().day.at("03:00").do(clear_log_file)
 
316
  while True:
317
  schedule.run_pending()
318
  time.sleep(60)
319
 
 
320
  def chatbot_fn(user_message, history, image=None):
321
  if history is None:
322
  history = []
 
 
323
  warehouse_stock_data = None
 
324
  try:
325
  warehouse_stock_data = get_warehouse_stock(user_message)
 
 
 
 
326
  except Exception as e:
327
  print(f"DEBUG - Warehouse stock error at start: {e}")
 
328
  try:
 
329
  if image is not None:
330
  user_message = process_image_message(image, user_message)
 
 
331
  comparison_result = handle_comparison_request(user_message)
332
  if comparison_result:
333
  yield comparison_result
334
  return
 
 
 
 
335
  except Exception as e:
336
  print(f"Enhanced features error: {e}")
 
 
 
337
  try:
338
  with file_lock:
339
  with open(LOG_FILE, 'a', encoding='utf-8') as f:
340
- f.write(f"User: {user_message}\n")
341
  except Exception as e:
342
  print(f"Dosya yazma hatası (Kullanıcı): {e}")
 
 
343
  system_messages = get_prompt_content_only()
 
 
344
  if document_content:
345
  system_messages.append({"role": "system", "content": f"Dökümanlardan gelen bilgiler: {document_content}"})
 
 
 
 
346
  product_found_improved = False
347
  if USE_IMPROVED_SEARCH and improved_bot:
348
  try:
349
  product_result = improved_bot.process_message(user_message)
350
  if product_result['is_product_query'] and product_result['response']:
 
351
  enhanced_response = product_result['response']
 
 
352
  if warehouse_stock_data and warehouse_stock_data != ["Hiçbir mağazada mevcut değil"]:
353
  warehouse_info = f"\n\n🏪 MAĞAZA STOK BİLGİLERİ:\n"
354
  for store_info in warehouse_stock_data:
355
  warehouse_info += f"• {store_info}\n"
356
  enhanced_response += warehouse_info
 
357
  elif warehouse_stock_data == ["Hiçbir mağazada mevcut değil"]:
358
  enhanced_response += f"\n\n🏪 MAĞAZA STOK BİLGİLERİ: Hiçbir mağazada mevcut değil"
359
- system_messages.append({"role": "system", "content": f"ÜRÜN BİLGİSİ:\n{enhanced_response}\n\nBu bilgileri kullanarak kullanıcıya yardımcı ol."})
 
 
 
 
 
360
  product_found_improved = True
361
  except Exception as e:
362
  print(f"Improved search error: {e}")
 
 
363
  if not product_found_improved:
 
 
 
364
  if warehouse_stock_data and warehouse_stock_data != ["Hiçbir mağazada mevcut değil"]:
365
  warehouse_info = f"🏪 MAĞAZA STOK BİLGİLERİ:\n"
366
  for store_info in warehouse_stock_data:
367
  warehouse_info += f"• {store_info}\n"
368
- system_messages.append({"role": "system", "content": f"GÜNCEL STOK DURUMU:\n{warehouse_info}\n\nBu bilgileri kullanarak kullanıcıya hangi mağazada stok olduğunu söyle."})
 
 
 
 
369
  elif warehouse_stock_data == ["Hiçbir mağazada mevcut değil"]:
370
- system_messages.append({"role": "system", "content": "🏪 MAĞAZA STOK BİLGİLERİ: Sorduğunuz ürün hiçbir mağazada mevcut değil."})
 
 
 
 
 
 
 
371
  messages = system_messages + history + [{"role": "user", "content": user_message}]
372
- if not OPENAI_API_KEY:
373
- yield "API anahtarı eksik."
374
- return
375
  payload = {
376
  "model": "gpt-4.1",
377
  "messages": messages,
@@ -386,11 +800,14 @@ def chatbot_fn(user_message, history, image=None):
386
  "Content-Type": "application/json",
387
  "Authorization": f"Bearer {OPENAI_API_KEY}"
388
  }
 
389
  response = requests.post(API_URL, headers=headers, json=payload, stream=True)
390
  if response.status_code != 200:
391
  yield "Bir hata oluştu."
392
  return
 
393
  partial_response = ""
 
394
  for chunk in response.iter_lines():
395
  if not chunk:
396
  continue
@@ -401,161 +818,456 @@ def chatbot_fn(user_message, history, image=None):
401
  delta = chunk_data['choices'][0]['delta']
402
  if 'content' in delta:
403
  partial_response += delta['content']
 
404
  formatted_response = extract_product_info_for_gallery(partial_response)
405
  yield formatted_response
406
  except json.JSONDecodeError as e:
407
  print(f"JSON parse hatası: {e} - Chunk: {chunk_str}")
408
  elif chunk_str == "data: [DONE]":
409
  break
 
 
410
  final_response = extract_product_info_for_gallery(partial_response)
411
  yield final_response
 
 
 
 
412
  try:
413
  with file_lock:
414
  with open(LOG_FILE, 'a', encoding='utf-8') as f:
415
- f.write(f"Bot: {partial_response}\n")
416
  except Exception as e:
417
  print(f"Dosya yazma hatası (Bot): {e}")
 
 
418
  with history_lock:
419
  global_chat_history.append({"role": "user", "content": user_message})
420
  global_chat_history.append({"role": "assistant", "content": partial_response})
421
 
 
422
  def slow_echo(message, history):
423
  for i in range(len(message)):
424
  time.sleep(0.05)
425
  yield "You typed: " + message[: i + 1]
426
 
 
427
  USE_SLOW_ECHO = False
428
  chat_fn = slow_echo if USE_SLOW_ECHO else chatbot_fn
429
 
430
- if 'APP_INIT_DONE' not in globals():
431
- APP_INIT_DONE = True
432
- products = load_products()
433
- print(f"Toplam {len(products)} ürün yüklendi.")
434
- initialize_enhanced_features(OPENAI_API_KEY, products)
435
- save_conversations_json()
436
- if GOOGLE_CREDENTIALS_PATH:
437
- document_thread = threading.Thread(target=download_documents_from_drive, daemon=True)
438
- document_thread.start()
439
- else:
440
- print("Google credentials dosyası bulunamadı.")
441
- if not USE_SLOW_ECHO:
442
- scheduler_thread = threading.Thread(target=run_scheduler, args=(global_chat_history,), daemon=True)
443
- scheduler_thread.start()
444
- if USE_IMPROVED_SEARCH:
445
- try:
446
- improved_bot = ImprovedChatbot(products)
447
- print("Improved product search initialized successfully")
448
- except Exception as e:
449
- print(f"Failed to initialize improved search: {e}")
450
- USE_IMPROVED_SEARCH = False
451
-
452
  custom_css = """
453
- .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; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
454
  """
455
 
 
456
  storage_js = ""
457
 
 
458
  def enhanced_chatbot_fn(message, history, image):
459
  return chatbot_fn(message, history, image)
460
 
 
461
  with gr.Blocks(css=custom_css, theme="soft", title="Trek Asistanı", head=storage_js) as demo:
462
  gr.Markdown("# 🚲 Trek Asistanı AI")
463
  gr.Markdown("**Akıllı özellikler:** Ürün karşılaştırması ve detaylı ürün bilgileri sunuyorum.")
 
 
 
464
  chatbot = gr.Chatbot(height=600, elem_id="chatbot", show_label=False, type="messages")
465
- msg = gr.Textbox(placeholder="Trek bisikletleri hakkında soru sorun...", show_label=False, elem_id="msg-input")
 
 
 
 
 
 
 
 
 
466
  def respond(message, chat_history):
467
  if not message.strip():
468
  return "", chat_history
 
 
469
  if chat_history is None:
470
  chat_history = []
 
 
471
  chat_history.append({"role": "user", "content": message})
472
  yield "", chat_history
 
 
473
  formatted_history = []
474
- for m in chat_history[:-1]:
475
- formatted_history.append(m)
 
476
  try:
 
477
  response_generator = chatbot_fn(message, formatted_history, None)
 
 
478
  response = ""
479
  for partial in response_generator:
480
  response = partial
 
 
481
  if chat_history[-1]["role"] == "user":
482
  chat_history.append({"role": "assistant", "content": response})
483
  else:
 
484
  chat_history[-1]["content"] = response
485
  yield "", chat_history
 
 
486
  try:
487
  add_conversation(message, response)
488
- save_conversations_json()
489
  except Exception as e:
490
  print(f"Error saving conversation: {e}")
 
491
  except Exception as e:
492
  error_msg = f"Üzgünüm, bir hata oluştu: {str(e)}"
493
  print(f"Chat error: {e}")
 
494
  if chat_history[-1]["role"] == "user":
495
  chat_history.append({"role": "assistant", "content": error_msg})
496
  else:
497
  chat_history[-1]["content"] = error_msg
498
  yield "", chat_history
 
499
  msg.submit(respond, [msg, chatbot], [msg, chatbot], show_progress=True)
 
 
500
  with gr.Accordion("📊 Konuşma Geçmişi (JSON)", open=False):
501
  with gr.Row():
502
  refresh_json_btn = gr.Button("🔄 Yenile", scale=1)
503
  download_json_btn = gr.Button("💾 JSON İndir", scale=1)
504
  view_dashboard_btn = gr.Button("📈 Dashboard'u Aç", scale=1)
 
505
  json_display = gr.JSON(label="Konuşmalar", elem_id="json_viewer")
506
  download_file = gr.File(label="İndir", visible=False)
 
507
  def get_conversations_json():
508
  from conversation_tracker import load_conversations
509
  convs = load_conversations()
 
510
  import json as json_module
511
  with open("temp_conversations.json", "w", encoding="utf-8") as f:
512
  json_module.dump(convs, f, ensure_ascii=False, indent=2)
513
  return convs
 
514
  def download_conversations():
515
- get_conversations_json()
516
  return gr.update(visible=True, value="temp_conversations.json")
 
517
  def open_dashboard():
518
  return gr.HTML("""
519
  <div style='padding: 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 10px; color: white;'>
520
  <h3>📊 Dashboard Kullanım Talimatları</h3>
521
  <ol style='margin: 15px 0;'>
522
- <li>Standalone HTML'i (bf_dashboard_auto.html) bilgisayarında aç</li>
523
- <li>HTML, Space'teki CORS açık API'den JSON'u otomatik çekecek</li>
 
 
524
  </ol>
 
 
 
 
 
 
525
  </div>
526
  """)
 
527
  refresh_json_btn.click(get_conversations_json, outputs=json_display)
528
- download_conversations_btn = download_json_btn
529
- download_conversations_btn.click(download_conversations, outputs=download_file)
530
  view_dashboard_btn.click(open_dashboard, outputs=json_display)
 
 
531
  demo.load(get_conversations_json, outputs=json_display)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
532
 
533
- api = FastAPI()
534
- api.add_middleware(
535
- CORSMiddleware,
536
- allow_origins=["*"],
537
- allow_credentials=False,
538
- allow_methods=["GET"],
539
- allow_headers=["*"],
540
- )
541
-
542
- def _read_json_safely(path):
543
- try:
544
- with open(path, "r", encoding="utf-8") as f:
545
- return f.read(), 200
546
- except FileNotFoundError:
547
- return json.dumps([]), 200
548
- except Exception as e:
549
- return json.dumps({"error": str(e)}), 500
550
-
551
- @api.get("/api/conversations")
552
- def api_conversations():
553
- body, code = _read_json_safely("conversations.json")
554
- return Response(content=body, media_type="application/json", status_code=code)
555
 
556
- @api.get("/api/public/conversations")
557
- def api_public_conversations():
558
- body, code = _read_json_safely("public/conversations.json")
559
- return Response(content=body, media_type="application/json", status_code=code)
560
 
561
- app = gr.mount_gradio_app(api, demo, path="/")
 
 
 
 
 
 
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
12
  from docx import Document
13
+ import spaces
14
  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
+
22
+ # Prompt dosyasını import et
23
  from prompts import get_prompt_content_only
24
+
25
+ # Enhanced features import et (sadece temel özellikler)
26
+ from enhanced_features import (
27
+ initialize_enhanced_features, process_image_message,
28
+ handle_comparison_request
29
+ )
30
  from image_renderer import extract_product_info_for_gallery, format_message_with_images
31
+
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:
39
+ from smart_warehouse_with_price import get_warehouse_stock_smart_with_price
40
+ get_warehouse_stock_smart = get_warehouse_stock_smart_with_price
41
+ except ImportError:
42
+ try:
43
+ from smart_warehouse import get_warehouse_stock_smart
44
+ except ImportError:
45
+ get_warehouse_stock_smart = None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
 
47
  def get_warehouse_stock(product_name):
48
+ """Use GPT intelligence to find warehouse stock"""
49
+ # First try GPT-powered search
50
+ if get_warehouse_stock_smart:
51
+ result = get_warehouse_stock_smart(product_name)
52
+ if result:
53
+ return result
54
+
55
+ # Fallback to old method
 
 
 
 
 
56
  return get_warehouse_stock_old(product_name)
57
 
58
+ # OLD warehouse stock finder - general algorithm
59
  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
67
  for attempt in range(3):
68
  try:
69
  url = 'https://video.trek-turkey.com/bizimhesap-warehouse-xml-b2b-api-v2.php'
70
+ timeout_val = 10 + (attempt * 5) # 10, 15, 20 seconds
71
  response = requests.get(url, verify=False, timeout=timeout_val)
72
  xml_text = response.text
73
  break
 
76
  return None
77
  except Exception:
78
  return None
79
+
80
+ # Turkish normalize
81
  def normalize(text):
82
  tr_map = {'ı': 'i', 'ğ': 'g', 'ü': 'u', 'ş': 's', 'ö': 'o', 'ç': 'c', 'İ': 'i', 'I': 'i'}
83
  text = text.lower()
84
  for tr, en in tr_map.items():
85
  text = text.replace(tr, en)
86
  return text
87
+
88
+ # Parse query
89
  query = normalize(product_name.strip()).replace('(2026)', '').replace('(2025)', '').strip()
90
  words = query.split()
91
+
92
+ # Find size markers (S, M, L, etc.)
93
  sizes = ['s', 'm', 'l', 'xl', 'xs', 'xxl', 'ml']
94
  size = next((w for w in words if w in sizes), None)
95
+
96
+ # Smart filtering: Keep only meaningful product identifiers
97
  product_words = []
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}")
140
+
141
+ # Find all Product blocks in XML
142
+ product_pattern = r'<Product>(.*?)</Product>'
143
+ all_products = re.findall(product_pattern, xml_text, re.DOTALL)
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
155
+
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)
193
+
194
  for wh_name, wh_stock in warehouses:
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
  display = "Bahçeköy mağazası"
207
  else:
208
  display = wh_name
209
+
210
  warehouse_info.append(f"{display}: Mevcut")
211
+ except:
212
  pass
213
+
214
  return warehouse_info if warehouse_info else ["Hiçbir mağazada mevcut değil"]
215
  else:
216
+ print(f"DEBUG - No match found for {' '.join(product_words)}")
217
  return ["Hiçbir mağazada mevcut değil"]
218
+
219
  except Exception as e:
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
425
+ USE_IMPROVED_SEARCH = True
426
+ print("DEBUG - Improved chatbot loaded successfully")
427
+ except ImportError as e:
428
+ print(f"DEBUG - Improved chatbot not available: {e}, using basic search")
429
+ USE_IMPROVED_SEARCH = False
430
+
431
+ # Gradio uyarılarını bastır
432
+ warnings.filterwarnings("ignore", category=UserWarning, module="gradio.components.chatbot")
433
+
434
+ # Log dosyası adı ve yolu
435
+ LOG_FILE = '/data/chat_logs.txt' if os.path.exists('/data') else 'chat_logs.txt'
436
+ print(f"Dosya yolu: {os.path.abspath(LOG_FILE)}")
437
+
438
+ # API ayarları
439
+ API_URL = "https://api.openai.com/v1/chat/completions"
440
+ OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
441
+ if not OPENAI_API_KEY:
442
+ print("Hata: OPENAI_API_KEY çevre değişkeni ayarlanmamış!")
443
+
444
+ # Trek bisiklet ürünlerini çekme
445
+ url = 'https://www.trekbisiklet.com.tr/output/8582384479'
446
+ response = requests.get(url, verify=False, timeout=30)
447
+ if response.status_code == 200 and response.content:
448
+ root = ET.fromstring(response.content)
449
+ else:
450
+ print(f"HTTP hatası: {response.status_code}")
451
+ root = None
452
+
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]
465
+ full_name = ' '.join(name_words)
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):
522
+ price_eft = price_eft_str
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):
543
+ price_rebate = price_rebate_str
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):
564
+ price_rebate_money_order = price_rebate_money_order_str
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))
585
+
586
+ print(f"Toplam {len(products)} ürün yüklendi.")
587
+
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:
594
+ try:
595
+ improved_bot = ImprovedChatbot(products)
596
+ print("Improved product search initialized successfully")
597
  except Exception as e:
598
+ print(f"Failed to initialize improved search: {e}")
599
+ USE_IMPROVED_SEARCH = False
600
+
601
+ # Google Drive API ayarları
602
+ GOOGLE_CREDENTIALS_PATH = os.getenv("GOOGLE_CREDENTIALS_PATH")
603
+ GOOGLE_FOLDER_ID = "1bE8aMj8-eFGftjMPOF8bKQJAhfHa0BN8"
604
+
605
+ # Global değişkenler
606
+ file_lock = threading.Lock()
607
+ history_lock = threading.Lock()
608
+ global_chat_history = []
609
+ document_content = ""
610
 
611
+ # Google Drive'dan döküman indirme fonksiyonu
612
  def download_documents_from_drive():
613
  global document_content
614
+
615
  if not GOOGLE_CREDENTIALS_PATH:
616
  print("Google credentials dosyası bulunamadı.")
617
  return
618
+
619
  try:
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)"
627
+ ).execute()
628
+
629
  files = results.get('files', [])
630
  all_content = []
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()
639
  downloader = MediaIoBaseDownload(file_io, request)
640
+
641
  done = False
642
  while done is False:
643
  status, done = downloader.next_chunk()
644
+
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:
659
  print(f"Google Drive'dan döküman indirme hatası: {e}")
660
 
661
+ # Döküman indirme işlemini arka planda çalıştır
662
+ document_thread = threading.Thread(target=download_documents_from_drive, daemon=True)
663
+ document_thread.start()
664
+
665
+ # Log dosyasını zamanla temizleme fonksiyonu
666
  def clear_log_file():
667
  try:
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}")
675
 
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)
683
 
684
+ # Chatbot fonksiyonu
685
  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:
749
  warehouse_info += f"• {store_info}\n"
750
  enhanced_response += warehouse_info
751
+ print(f"DEBUG - Added warehouse stock to improved search response")
752
  elif warehouse_stock_data == ["Hiçbir mağazada mevcut değil"]:
753
  enhanced_response += f"\n\n🏪 MAĞAZA STOK BİLGİLERİ: Hiçbir mağazada mevcut değil"
754
+ print(f"DEBUG - No stock available (improved search)")
755
+
756
+ system_messages.append({
757
+ "role": "system",
758
+ "content": f"ÜRÜN BİLGİSİ:\n{enhanced_response}\n\nBu bilgileri kullanarak kullanıcıya yardımcı ol."
759
+ })
760
  product_found_improved = True
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:
772
  warehouse_info += f"• {store_info}\n"
773
+ system_messages.append({
774
+ "role": "system",
775
+ "content": f"GÜNCEL STOK DURUMU:\n{warehouse_info}\n\nBu bilgileri kullanarak kullanıcıya hangi mağazada stok olduğunu söyle."
776
+ })
777
+ print(f"DEBUG - Using warehouse stock data")
778
  elif warehouse_stock_data == ["Hiçbir mağazada mevcut değil"]:
779
+ system_messages.append({
780
+ "role": "system",
781
+ "content": "🏪 MAĞAZA STOK BİLGİLERİ: Sorduğunuz ürün hiçbir mağazada mevcut değil."
782
+ })
783
+ print(f"DEBUG - Product not available in any store")
784
+ else:
785
+ print(f"DEBUG - No warehouse stock data available")
786
+
787
  messages = system_messages + history + [{"role": "user", "content": user_message}]
788
+
 
 
789
  payload = {
790
  "model": "gpt-4.1",
791
  "messages": messages,
 
800
  "Content-Type": "application/json",
801
  "Authorization": f"Bearer {OPENAI_API_KEY}"
802
  }
803
+
804
  response = requests.post(API_URL, headers=headers, json=payload, stream=True)
805
  if response.status_code != 200:
806
  yield "Bir hata oluştu."
807
  return
808
+
809
  partial_response = ""
810
+
811
  for chunk in response.iter_lines():
812
  if not chunk:
813
  continue
 
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:
825
  print(f"JSON parse hatası: {e} - Chunk: {chunk_str}")
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})
847
 
848
+ # Slow echo (test için)
849
  def slow_echo(message, history):
850
  for i in range(len(message)):
851
  time.sleep(0.05)
852
  yield "You typed: " + message[: i + 1]
853
 
854
+ # Kullanım modu
855
  USE_SLOW_ECHO = False
856
  chat_fn = slow_echo if USE_SLOW_ECHO else chatbot_fn
857
 
858
+ 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(
1078
+ placeholder="Trek bisikletleri hakkında soru sorun...",
1079
+ show_label=False,
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:
1133
  chat_history[-1]["content"] = error_msg
1134
  yield "", chat_history
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)
1142
  download_json_btn = gr.Button("💾 JSON İndir", scale=1)
1143
  view_dashboard_btn = gr.Button("📈 Dashboard'u Aç", scale=1)
1144
+
1145
  json_display = gr.JSON(label="Konuşmalar", elem_id="json_viewer")
1146
  download_file = gr.File(label="İndir", visible=False)
1147
+
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():
1162
  return gr.HTML("""
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
+ )