SamiKoen commited on
Commit
90ec341
·
1 Parent(s): 03755da

Fiyat ve link sistemi eklendi - Trek XML'den doğru fiyat ve link bilgisi

Browse files
Files changed (2) hide show
  1. app.py +7 -3
  2. smart_warehouse_with_price.py +352 -0
app.py CHANGED
@@ -29,11 +29,15 @@ from enhanced_features import (
29
  )
30
  from image_renderer import extract_product_info_for_gallery, format_message_with_images
31
 
32
- # Import smart warehouse with GPT intelligence
33
  try:
34
- from smart_warehouse import get_warehouse_stock_smart
 
35
  except ImportError:
36
- get_warehouse_stock_smart = None
 
 
 
37
 
38
  def get_warehouse_stock(product_name):
39
  """Use GPT intelligence to find warehouse stock"""
 
29
  )
30
  from image_renderer import extract_product_info_for_gallery, format_message_with_images
31
 
32
+ # Import smart warehouse with GPT intelligence and price
33
  try:
34
+ from smart_warehouse_with_price import get_warehouse_stock_smart_with_price
35
+ get_warehouse_stock_smart = get_warehouse_stock_smart_with_price
36
  except ImportError:
37
+ try:
38
+ from smart_warehouse import get_warehouse_stock_smart
39
+ except ImportError:
40
+ get_warehouse_stock_smart = None
41
 
42
  def get_warehouse_stock(product_name):
43
  """Use GPT intelligence to find warehouse stock"""
smart_warehouse_with_price.py ADDED
@@ -0,0 +1,352 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Smart warehouse stock finder with price and link information"""
2
+
3
+ import requests
4
+ import re
5
+ import os
6
+ import json
7
+ import xml.etree.ElementTree as ET
8
+
9
+ def get_product_price_and_link(product_name, variant=None):
10
+ """Get price and link from Trek website XML"""
11
+ try:
12
+ # Get Trek product catalog
13
+ url = 'https://www.trekbisiklet.com.tr/output/8582384479'
14
+ response = requests.get(url, verify=False, timeout=10)
15
+
16
+ if response.status_code != 200:
17
+ return None, None
18
+
19
+ root = ET.fromstring(response.content)
20
+
21
+ # Normalize search terms
22
+ search_name = product_name.lower()
23
+ search_variant = variant.lower() if variant else ""
24
+
25
+ # Turkish character normalization
26
+ tr_map = {'ı': 'i', 'ğ': 'g', 'ü': 'u', 'ş': 's', 'ö': 'o', 'ç': 'c'}
27
+ for tr, en in tr_map.items():
28
+ search_name = search_name.replace(tr, en)
29
+ search_variant = search_variant.replace(tr, en)
30
+
31
+ best_match = None
32
+ best_score = 0
33
+
34
+ for item in root.findall('item'):
35
+ # Get product name
36
+ rootlabel_elem = item.find('rootlabel')
37
+ if rootlabel_elem is None or not rootlabel_elem.text:
38
+ continue
39
+
40
+ item_name = rootlabel_elem.text.lower()
41
+ for tr, en in tr_map.items():
42
+ item_name = item_name.replace(tr, en)
43
+
44
+ # Calculate match score
45
+ score = 0
46
+ name_parts = search_name.split()
47
+ for part in name_parts:
48
+ if part in item_name:
49
+ score += 1
50
+
51
+ # Check variant if specified
52
+ if variant and search_variant in item_name:
53
+ score += 2 # Variant match is important
54
+
55
+ if score > best_score:
56
+ best_score = score
57
+ best_match = item
58
+
59
+ if best_match and best_score > 0:
60
+ # Extract price
61
+ price_elem = best_match.find('priceTaxWithCur')
62
+ price = price_elem.text if price_elem is not None and price_elem.text else None
63
+
64
+ # Round price
65
+ if price:
66
+ try:
67
+ price_float = float(price)
68
+ if price_float > 200000:
69
+ price = f"{round(price_float / 5000) * 5000:,.0f} TL"
70
+ elif price_float > 30000:
71
+ price = f"{round(price_float / 1000) * 1000:,.0f} TL"
72
+ elif price_float > 10000:
73
+ price = f"{round(price_float / 100) * 100:,.0f} TL"
74
+ else:
75
+ price = f"{round(price_float / 10) * 10:,.0f} TL"
76
+ except:
77
+ price = f"{price} TL"
78
+
79
+ # Extract link
80
+ link_elem = best_match.find('productUrl')
81
+ link = link_elem.text if link_elem is not None and link_elem.text else None
82
+
83
+ return price, link
84
+
85
+ return None, None
86
+
87
+ except Exception as e:
88
+ print(f"Error getting price/link: {e}")
89
+ return None, None
90
+
91
+ def get_warehouse_stock_smart_with_price(user_message, previous_result=None):
92
+ """Enhanced smart warehouse search with price and link info"""
93
+
94
+ OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
95
+
96
+ # Check if user is asking about specific warehouse
97
+ warehouse_keywords = {
98
+ 'caddebostan': 'Caddebostan',
99
+ 'ortaköy': 'Ortaköy',
100
+ 'ortakoy': 'Ortaköy',
101
+ 'alsancak': 'Alsancak',
102
+ 'izmir': 'Alsancak',
103
+ 'bahçeköy': 'Bahçeköy',
104
+ 'bahcekoy': 'Bahçeköy'
105
+ }
106
+
107
+ user_lower = user_message.lower()
108
+ asked_warehouse = None
109
+ for keyword, warehouse in warehouse_keywords.items():
110
+ if keyword in user_lower:
111
+ asked_warehouse = warehouse
112
+ break
113
+
114
+ # Get XML data with retry
115
+ xml_text = None
116
+ for attempt in range(3):
117
+ try:
118
+ url = 'https://video.trek-turkey.com/bizimhesap-warehouse-xml-b2b-api-v2.php'
119
+ timeout_val = 10 + (attempt * 5)
120
+ response = requests.get(url, verify=False, timeout=timeout_val)
121
+ xml_text = response.text
122
+ print(f"DEBUG - XML fetched: {len(xml_text)} characters (attempt {attempt+1})")
123
+ break
124
+ except requests.exceptions.Timeout:
125
+ print(f"XML fetch timeout (attempt {attempt+1}/3, timeout={timeout_val}s)")
126
+ if attempt == 2:
127
+ print("All attempts failed - timeout")
128
+ return None
129
+ except Exception as e:
130
+ print(f"XML fetch error: {e}")
131
+ return None
132
+
133
+ # Extract product blocks
134
+ product_pattern = r'<Product>(.*?)</Product>'
135
+ all_products = re.findall(product_pattern, xml_text, re.DOTALL)
136
+
137
+ # Create simplified product list for GPT
138
+ products_summary = []
139
+ for i, product_block in enumerate(all_products):
140
+ name_match = re.search(r'<ProductName><!\[CDATA\[(.*?)\]\]></ProductName>', product_block)
141
+ variant_match = re.search(r'<ProductVariant><!\[CDATA\[(.*?)\]\]></ProductVariant>', product_block)
142
+
143
+ if name_match:
144
+ warehouses_with_stock = []
145
+ warehouse_regex = r'<Warehouse>.*?<Name><!\[CDATA\[(.*?)\]\]></Name>.*?<Stock>(.*?)</Stock>.*?</Warehouse>'
146
+ warehouses = re.findall(warehouse_regex, product_block, re.DOTALL)
147
+
148
+ for wh_name, wh_stock in warehouses:
149
+ try:
150
+ if int(wh_stock.strip()) > 0:
151
+ warehouses_with_stock.append(wh_name)
152
+ except:
153
+ pass
154
+
155
+ product_info = {
156
+ "index": i,
157
+ "name": name_match.group(1),
158
+ "variant": variant_match.group(1) if variant_match else "",
159
+ "warehouses": warehouses_with_stock
160
+ }
161
+ products_summary.append(product_info)
162
+
163
+ # Prepare warehouse filter if needed
164
+ warehouse_filter = ""
165
+ if asked_warehouse:
166
+ warehouse_filter = f"\nIMPORTANT: User is asking specifically about {asked_warehouse} warehouse. Only return products available in that warehouse."
167
+
168
+ # GPT-5 prompt with enhanced instructions
169
+ smart_prompt = f"""User is asking: "{user_message}"
170
+
171
+ Find ALL products that match this query from the list below.
172
+ If user asks about specific size (S, M, L, XL, XXL, SMALL, MEDIUM, LARGE, X-LARGE), return only that size.
173
+ If user asks generally (without size), return ALL variants of the product.
174
+ {warehouse_filter}
175
+
176
+ IMPORTANT BRAND AND PRODUCT TYPE RULES:
177
+ - GOBIK: Spanish textile brand we import. When user asks about "gobik", return ALL products with "GOBIK" in the name.
178
+ - Product names contain type information: FORMA (jersey/cycling shirt), TAYT (tights), İÇLİK (base layer), YAĞMURLUK (raincoat), etc.
179
+ - Understand Turkish/English terms:
180
+ * "erkek forma" / "men's jersey" -> Find products with FORMA in name
181
+ * "tayt" / "tights" -> Find products with TAYT in name
182
+ * "içlik" / "base layer" -> Find products with İÇLİK in name
183
+ * "yağmurluk" / "raincoat" -> Find products with YAĞMURLUK in name
184
+ - Gender: UNISEX means for both men and women. If no gender specified, it's typically men's.
185
+
186
+ Products list (with warehouse availability):
187
+ {json.dumps(products_summary, ensure_ascii=False, indent=2)}
188
+
189
+ Return index numbers of ALL matching products as comma-separated list (e.g., "5,8,12,15").
190
+ If no products found, return: -1"""
191
+
192
+ headers = {
193
+ "Content-Type": "application/json",
194
+ "Authorization": f"Bearer {OPENAI_API_KEY}"
195
+ }
196
+
197
+ payload = {
198
+ "model": "gpt-5-chat-latest",
199
+ "messages": [
200
+ {"role": "system", "content": "You are a product matcher. Find ALL matching products. Return only index numbers."},
201
+ {"role": "user", "content": smart_prompt}
202
+ ],
203
+ "temperature": 0,
204
+ "max_tokens": 100
205
+ }
206
+
207
+ try:
208
+ response = requests.post(
209
+ "https://api.openai.com/v1/chat/completions",
210
+ headers=headers,
211
+ json=payload,
212
+ timeout=10
213
+ )
214
+
215
+ if response.status_code == 200:
216
+ result = response.json()
217
+ indices_str = result['choices'][0]['message']['content'].strip()
218
+
219
+ if indices_str == "-1":
220
+ return ["Ürün bulunamadı"]
221
+
222
+ try:
223
+ indices = [int(idx.strip()) for idx in indices_str.split(',')]
224
+
225
+ # Collect all matching products with price/link
226
+ all_variants = []
227
+ warehouse_stock = {}
228
+
229
+ for idx in indices:
230
+ if 0 <= idx < len(all_products):
231
+ product_block = all_products[idx]
232
+
233
+ # Get product details
234
+ name_match = re.search(r'<ProductName><!\[CDATA\[(.*?)\]\]></ProductName>', product_block)
235
+ variant_match = re.search(r'<ProductVariant><!\[CDATA\[(.*?)\]\]></ProductVariant>', product_block)
236
+
237
+ if name_match:
238
+ product_name = name_match.group(1)
239
+ variant = variant_match.group(1) if variant_match else ""
240
+
241
+ # Get price and link from Trek website
242
+ price, link = get_product_price_and_link(product_name, variant)
243
+
244
+ variant_info = {
245
+ 'name': product_name,
246
+ 'variant': variant,
247
+ 'price': price,
248
+ 'link': link,
249
+ 'warehouses': []
250
+ }
251
+
252
+ # Get warehouse stock
253
+ warehouse_regex = r'<Warehouse>.*?<Name><!\[CDATA\[(.*?)\]\]></Name>.*?<Stock>(.*?)</Stock>.*?</Warehouse>'
254
+ warehouses = re.findall(warehouse_regex, product_block, re.DOTALL)
255
+
256
+ for wh_name, wh_stock in warehouses:
257
+ try:
258
+ stock = int(wh_stock.strip())
259
+ if stock > 0:
260
+ display_name = format_warehouse_name(wh_name)
261
+ variant_info['warehouses'].append({
262
+ 'name': display_name,
263
+ 'stock': stock
264
+ })
265
+
266
+ if display_name not in warehouse_stock:
267
+ warehouse_stock[display_name] = 0
268
+ warehouse_stock[display_name] += stock
269
+ except:
270
+ pass
271
+
272
+ if variant_info['warehouses']:
273
+ all_variants.append(variant_info)
274
+
275
+ # Format result
276
+ result = []
277
+
278
+ if asked_warehouse:
279
+ # Filter for specific warehouse
280
+ warehouse_variants = []
281
+ for variant in all_variants:
282
+ for wh in variant['warehouses']:
283
+ if asked_warehouse in wh['name']:
284
+ warehouse_variants.append(variant)
285
+ break
286
+
287
+ if warehouse_variants:
288
+ result.append(f"{format_warehouse_name(asked_warehouse)} mağazasında mevcut:")
289
+ for v in warehouse_variants:
290
+ variant_text = f" ({v['variant']})" if v['variant'] else ""
291
+ result.append(f"• {v['name']}{variant_text}")
292
+ if v['price']:
293
+ result.append(f" Fiyat: {v['price']}")
294
+ if v['link']:
295
+ result.append(f" Link: {v['link']}")
296
+ else:
297
+ result.append(f"{format_warehouse_name(asked_warehouse)} mağazasında bu ürün mevcut değil")
298
+ else:
299
+ # Show all variants
300
+ if all_variants:
301
+ # Group by product name for cleaner display
302
+ product_groups = {}
303
+ for variant in all_variants:
304
+ if variant['name'] not in product_groups:
305
+ product_groups[variant['name']] = []
306
+ product_groups[variant['name']].append(variant)
307
+
308
+ result.append(f"Bulunan ürünler:")
309
+
310
+ for product_name, variants in product_groups.items():
311
+ result.append(f"\n{product_name}:")
312
+
313
+ # Show first variant's price and link (usually same for all variants)
314
+ if variants[0]['price']:
315
+ result.append(f"Fiyat: {variants[0]['price']}")
316
+ if variants[0]['link']:
317
+ result.append(f"Link: {variants[0]['link']}")
318
+
319
+ # Show variants and their availability
320
+ for v in variants:
321
+ if v['variant']:
322
+ warehouses_str = ", ".join([w['name'].replace(' mağazası', '') for w in v['warehouses']])
323
+ result.append(f"• {v['variant']}: {warehouses_str}")
324
+
325
+ else:
326
+ result.append("Hiçbir mağazada stok yok")
327
+
328
+ return result
329
+
330
+ except (ValueError, IndexError) as e:
331
+ print(f"DEBUG - Error parsing indices: {e}")
332
+ return None
333
+ else:
334
+ print(f"GPT API error: {response.status_code}")
335
+ return None
336
+
337
+ except Exception as e:
338
+ print(f"Error calling GPT: {e}")
339
+ return None
340
+
341
+ def format_warehouse_name(wh_name):
342
+ """Format warehouse name nicely"""
343
+ if "CADDEBOSTAN" in wh_name:
344
+ return "Caddebostan mağazası"
345
+ elif "ORTAKÖY" in wh_name:
346
+ return "Ortaköy mağazası"
347
+ elif "ALSANCAK" in wh_name:
348
+ return "İzmir Alsancak mağazası"
349
+ elif "BAHCEKOY" in wh_name or "BAHÇEKÖY" in wh_name:
350
+ return "Bahçeköy mağazası"
351
+ else:
352
+ return wh_name.replace("MAGAZA DEPO", "").strip()