fix: Add Trek XML fallback for products not in warehouse
Browse files- When GPT-5 returns -1 (no match in warehouse), search Trek XML directly
- Fixes Marlin 5 showing wrong price (was showing Rail price)
- Now correctly shows 53,000 TL for Marlin 5
- Added debug messages for better troubleshooting
The issue was: Marlin products aren't in warehouse XML, so GPT was returning Rail index, causing wrong price/link.
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <[email protected]>
- __pycache__/smart_warehouse_with_price.cpython-312.pyc +0 -0
- debug_marlin.py +95 -0
- debug_name_mismatch.py +56 -0
- debug_warehouse.py +60 -0
- smart_warehouse_with_price.py +14 -1
- test_actual_function.py +91 -0
__pycache__/smart_warehouse_with_price.cpython-312.pyc
CHANGED
Binary files a/__pycache__/smart_warehouse_with_price.cpython-312.pyc and b/__pycache__/smart_warehouse_with_price.cpython-312.pyc differ
|
|
debug_marlin.py
ADDED
@@ -0,0 +1,95 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/usr/bin/env python3
|
2 |
+
"""Debug why Marlin 5 returns Rail"""
|
3 |
+
|
4 |
+
import requests
|
5 |
+
import xml.etree.ElementTree as ET
|
6 |
+
import re
|
7 |
+
|
8 |
+
def debug_matching():
|
9 |
+
"""See what matches when searching for Marlin 5"""
|
10 |
+
|
11 |
+
# Get Trek XML
|
12 |
+
url = 'https://www.trekbisiklet.com.tr/output/8582384479'
|
13 |
+
response = requests.get(url, verify=False, timeout=10)
|
14 |
+
|
15 |
+
if response.status_code != 200:
|
16 |
+
print("Failed to fetch XML")
|
17 |
+
return
|
18 |
+
|
19 |
+
root = ET.fromstring(response.content)
|
20 |
+
|
21 |
+
# Search parameters (from smart_warehouse_with_price.py logic)
|
22 |
+
search_name = "marlin 5"
|
23 |
+
clean_search = re.sub(r'\s*\(\d{4}\)\s*', '', search_name).strip()
|
24 |
+
|
25 |
+
print(f"Searching for: '{search_name}'")
|
26 |
+
print(f"Clean search: '{clean_search}'")
|
27 |
+
print(f"Search parts: {clean_search.split()}")
|
28 |
+
print("\n" + "="*60)
|
29 |
+
|
30 |
+
matches = []
|
31 |
+
|
32 |
+
for item in root.findall('item'):
|
33 |
+
rootlabel_elem = item.find('rootlabel')
|
34 |
+
if rootlabel_elem is None or not rootlabel_elem.text:
|
35 |
+
continue
|
36 |
+
|
37 |
+
item_name = rootlabel_elem.text.lower()
|
38 |
+
clean_item = re.sub(r'\s*\(\d{4}\)\s*', '', item_name).strip()
|
39 |
+
|
40 |
+
# Calculate score (same logic as smart_warehouse_with_price.py)
|
41 |
+
score = 0
|
42 |
+
|
43 |
+
# Exact match
|
44 |
+
if clean_search == clean_item:
|
45 |
+
score += 100
|
46 |
+
# Starts with
|
47 |
+
elif clean_item.startswith(clean_search + " ") or clean_item == clean_search:
|
48 |
+
score += 50
|
49 |
+
else:
|
50 |
+
# Partial matching
|
51 |
+
name_parts = clean_search.split()
|
52 |
+
for part in name_parts:
|
53 |
+
if part in clean_item:
|
54 |
+
score += 1
|
55 |
+
|
56 |
+
if score > 0:
|
57 |
+
price_elem = item.find('priceTaxWithCur')
|
58 |
+
link_elem = item.find('productLink')
|
59 |
+
|
60 |
+
matches.append({
|
61 |
+
'original': rootlabel_elem.text,
|
62 |
+
'clean': clean_item,
|
63 |
+
'score': score,
|
64 |
+
'price': price_elem.text if price_elem is not None else 'N/A',
|
65 |
+
'link': link_elem.text if link_elem is not None else 'N/A'
|
66 |
+
})
|
67 |
+
|
68 |
+
# Sort by score
|
69 |
+
matches.sort(key=lambda x: x['score'], reverse=True)
|
70 |
+
|
71 |
+
print(f"\nFound {len(matches)} matches:")
|
72 |
+
print("="*60)
|
73 |
+
|
74 |
+
for i, match in enumerate(matches[:20], 1): # Show top 20
|
75 |
+
print(f"\n{i}. Score: {match['score']}")
|
76 |
+
print(f" Original: {match['original']}")
|
77 |
+
print(f" Clean: {match['clean']}")
|
78 |
+
print(f" Price: {match['price']}")
|
79 |
+
if match['link'] != 'N/A':
|
80 |
+
print(f" Link: ...{match['link'][-50:]}")
|
81 |
+
|
82 |
+
# Explain why it matched
|
83 |
+
print(" Why matched:")
|
84 |
+
if clean_search == match['clean']:
|
85 |
+
print(" - EXACT MATCH")
|
86 |
+
elif match['clean'].startswith(clean_search + " "):
|
87 |
+
print(" - STARTS WITH search")
|
88 |
+
else:
|
89 |
+
parts = clean_search.split()
|
90 |
+
for part in parts:
|
91 |
+
if part in match['clean']:
|
92 |
+
print(f" - Contains '{part}'")
|
93 |
+
|
94 |
+
if __name__ == "__main__":
|
95 |
+
debug_matching()
|
debug_name_mismatch.py
ADDED
@@ -0,0 +1,56 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/usr/bin/env python3
|
2 |
+
"""Debug product name mismatch between warehouse and Trek XML"""
|
3 |
+
|
4 |
+
import requests
|
5 |
+
import re
|
6 |
+
import xml.etree.ElementTree as ET
|
7 |
+
|
8 |
+
def check_name_mismatch():
|
9 |
+
"""Check if product names match between warehouse and Trek"""
|
10 |
+
|
11 |
+
# Get warehouse data (from PHP)
|
12 |
+
try:
|
13 |
+
warehouse_url = 'https://video.trek-turkey.com/bizimhesap-warehouse-xml-b2b-api-v2.php'
|
14 |
+
response = requests.get(warehouse_url, verify=False, timeout=10)
|
15 |
+
warehouse_xml = response.text
|
16 |
+
|
17 |
+
# Extract warehouse product names
|
18 |
+
product_pattern = r'<ProductName><!\[CDATA\[(.*?)\]\]></ProductName>'
|
19 |
+
warehouse_products = re.findall(product_pattern, warehouse_xml)
|
20 |
+
|
21 |
+
print(f"Warehouse products count: {len(warehouse_products)}")
|
22 |
+
print("\nSample warehouse products containing 'MARLIN' or 'RAIL':")
|
23 |
+
for p in warehouse_products:
|
24 |
+
if 'MARLIN' in p.upper() or 'RAIL' in p.upper():
|
25 |
+
print(f" - {p}")
|
26 |
+
|
27 |
+
except Exception as e:
|
28 |
+
print(f"Warehouse error: {e}")
|
29 |
+
|
30 |
+
print("\n" + "="*60)
|
31 |
+
|
32 |
+
# Get Trek XML data
|
33 |
+
try:
|
34 |
+
trek_url = 'https://www.trekbisiklet.com.tr/output/8582384479'
|
35 |
+
response = requests.get(trek_url, verify=False, timeout=10)
|
36 |
+
root = ET.fromstring(response.content)
|
37 |
+
|
38 |
+
trek_products = []
|
39 |
+
for item in root.findall('.//item'):
|
40 |
+
title = item.find('rootlabel')
|
41 |
+
if title is not None and title.text:
|
42 |
+
trek_products.append(title.text)
|
43 |
+
|
44 |
+
print(f"Trek products count: {len(trek_products)}")
|
45 |
+
print("\nSample Trek products containing 'MARLIN' or 'RAIL':")
|
46 |
+
for p in trek_products:
|
47 |
+
if 'MARLIN' in p.upper() or 'RAIL' in p.upper():
|
48 |
+
print(f" - {p}")
|
49 |
+
if len([x for x in trek_products if 'MARLIN' in x.upper() or 'RAIL' in x.upper()]) > 10:
|
50 |
+
break
|
51 |
+
|
52 |
+
except Exception as e:
|
53 |
+
print(f"Trek error: {e}")
|
54 |
+
|
55 |
+
if __name__ == "__main__":
|
56 |
+
check_name_mismatch()
|
debug_warehouse.py
ADDED
@@ -0,0 +1,60 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/usr/bin/env python3
|
2 |
+
"""Debug warehouse XML to understand product names"""
|
3 |
+
|
4 |
+
import requests
|
5 |
+
import re
|
6 |
+
|
7 |
+
def debug_warehouse():
|
8 |
+
"""Check what's in warehouse XML"""
|
9 |
+
|
10 |
+
url = 'https://veloconnect.wardin.com/v1/wardin?auth_token=97e0949096e88dd936f1bc2f0ffd3a07&auth_source=alatin.com.tr'
|
11 |
+
|
12 |
+
try:
|
13 |
+
response = requests.get(url, verify=False, timeout=10)
|
14 |
+
if response.status_code != 200:
|
15 |
+
print(f"Failed to fetch: {response.status_code}")
|
16 |
+
return
|
17 |
+
|
18 |
+
xml_text = response.text
|
19 |
+
print(f"XML size: {len(xml_text)} chars")
|
20 |
+
|
21 |
+
# Extract product names
|
22 |
+
product_pattern = r'<ProductName><!\[CDATA\[(.*?)\]\]></ProductName>'
|
23 |
+
products = re.findall(product_pattern, xml_text)
|
24 |
+
|
25 |
+
print(f"Total products: {len(products)}")
|
26 |
+
print("\n" + "="*60)
|
27 |
+
|
28 |
+
# Look for anything with "5" in it
|
29 |
+
print("Products containing '5':")
|
30 |
+
for p in products:
|
31 |
+
if '5' in p:
|
32 |
+
print(f" - {p}")
|
33 |
+
|
34 |
+
print("\n" + "="*60)
|
35 |
+
print("Products containing 'MARLIN' or 'marlin':")
|
36 |
+
for p in products:
|
37 |
+
if 'MARLIN' in p.upper():
|
38 |
+
print(f" - {p}")
|
39 |
+
|
40 |
+
print("\n" + "="*60)
|
41 |
+
print("First 20 Trek brand products:")
|
42 |
+
trek_products = [p for p in products if 'TREK' in p.upper() or 'FX' in p or 'DOMANE' in p.upper() or 'RAIL' in p.upper()]
|
43 |
+
for p in trek_products[:20]:
|
44 |
+
print(f" - {p}")
|
45 |
+
|
46 |
+
print("\n" + "="*60)
|
47 |
+
print("Looking for pattern variations of Marlin:")
|
48 |
+
patterns = ['MARLIN', 'MARLİN', 'MARL1N', 'M4RL1N', 'MARLYN']
|
49 |
+
for pattern in patterns:
|
50 |
+
found = [p for p in products if pattern in p.upper()]
|
51 |
+
if found:
|
52 |
+
print(f"\nPattern '{pattern}' found in:")
|
53 |
+
for f in found[:5]:
|
54 |
+
print(f" - {f}")
|
55 |
+
|
56 |
+
except Exception as e:
|
57 |
+
print(f"Error: {e}")
|
58 |
+
|
59 |
+
if __name__ == "__main__":
|
60 |
+
debug_warehouse()
|
smart_warehouse_with_price.py
CHANGED
@@ -339,8 +339,21 @@ Examples of correct responses:
|
|
339 |
|
340 |
print(f"DEBUG - GPT-5 response: '{indices_str}'")
|
341 |
|
342 |
-
# Handle empty response
|
343 |
if not indices_str or indices_str == "-1":
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
344 |
return ["Ürün bulunamadı"]
|
345 |
|
346 |
try:
|
|
|
339 |
|
340 |
print(f"DEBUG - GPT-5 response: '{indices_str}'")
|
341 |
|
342 |
+
# Handle empty response - try Trek XML as fallback
|
343 |
if not indices_str or indices_str == "-1":
|
344 |
+
print(f"DEBUG - No match in warehouse, trying Trek XML for '{user_message}'")
|
345 |
+
# Try to find in Trek XML directly
|
346 |
+
price, link = get_product_price_and_link(user_message)
|
347 |
+
if price and link:
|
348 |
+
# Found in Trek XML!
|
349 |
+
return [
|
350 |
+
f"🚲 **{user_message.title()}**",
|
351 |
+
f"💰 Fiyat: {price}",
|
352 |
+
f"🔗 Link: {link}",
|
353 |
+
"",
|
354 |
+
"📌 Not: Bu ürün şu anda mağaza stoklarında görünmüyor.",
|
355 |
+
"Güncel stok durumu için mağazalarımızı arayabilirsiniz."
|
356 |
+
]
|
357 |
return ["Ürün bulunamadı"]
|
358 |
|
359 |
try:
|
test_actual_function.py
ADDED
@@ -0,0 +1,91 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/usr/bin/env python3
|
2 |
+
"""Test actual get_product_price_and_link function"""
|
3 |
+
|
4 |
+
from smart_warehouse_with_price import get_product_price_and_link
|
5 |
+
|
6 |
+
# Test with debug prints
|
7 |
+
def test_with_debug():
|
8 |
+
print("Testing get_product_price_and_link('Marlin 5')...")
|
9 |
+
|
10 |
+
# Temporarily modify the function to add debug
|
11 |
+
import smart_warehouse_with_price
|
12 |
+
import xml.etree.ElementTree as ET
|
13 |
+
import re
|
14 |
+
|
15 |
+
# Get the XML
|
16 |
+
xml_content = smart_warehouse_with_price.get_cached_trek_xml()
|
17 |
+
if not xml_content:
|
18 |
+
print("No XML content")
|
19 |
+
return
|
20 |
+
|
21 |
+
root = ET.fromstring(xml_content)
|
22 |
+
|
23 |
+
search_name = "marlin 5"
|
24 |
+
clean_search = re.sub(r'\s*\(\d{4}\)\s*', '', search_name).strip()
|
25 |
+
|
26 |
+
print(f"Clean search: '{clean_search}'")
|
27 |
+
|
28 |
+
best_match = None
|
29 |
+
best_score = 0
|
30 |
+
best_name = None
|
31 |
+
|
32 |
+
count = 0
|
33 |
+
for item in root.findall('item'):
|
34 |
+
rootlabel_elem = item.find('rootlabel')
|
35 |
+
if rootlabel_elem is None or not rootlabel_elem.text:
|
36 |
+
continue
|
37 |
+
|
38 |
+
item_name = rootlabel_elem.text.lower()
|
39 |
+
|
40 |
+
# Turkish char normalization
|
41 |
+
tr_map = {'ı': 'i', 'ğ': 'g', 'ü': 'u', 'ş': 's', 'ö': 'o', 'ç': 'c'}
|
42 |
+
for tr, en in tr_map.items():
|
43 |
+
item_name = item_name.replace(tr, en)
|
44 |
+
|
45 |
+
clean_item = re.sub(r'\s*\(\d{4}\)\s*', '', item_name).strip()
|
46 |
+
|
47 |
+
score = 0
|
48 |
+
|
49 |
+
# Exact match
|
50 |
+
if clean_search == clean_item:
|
51 |
+
score += 100
|
52 |
+
elif clean_item.startswith(clean_search + " ") or clean_item == clean_search:
|
53 |
+
score += 50
|
54 |
+
else:
|
55 |
+
name_parts = clean_search.split()
|
56 |
+
for part in name_parts:
|
57 |
+
if part in clean_item:
|
58 |
+
score += 1
|
59 |
+
|
60 |
+
if score > 0:
|
61 |
+
count += 1
|
62 |
+
if count <= 10: # Show first 10 matches
|
63 |
+
print(f" {count}. {rootlabel_elem.text[:50]}: score={score}")
|
64 |
+
|
65 |
+
if score > best_score:
|
66 |
+
best_score = score
|
67 |
+
best_match = item
|
68 |
+
best_name = rootlabel_elem.text
|
69 |
+
print(f" >>> NEW BEST: {best_name} with score {best_score}")
|
70 |
+
|
71 |
+
print(f"\nFinal best match: {best_name}")
|
72 |
+
print(f"Best score: {best_score}")
|
73 |
+
|
74 |
+
if best_match is not None:
|
75 |
+
price_elem = best_match.find('priceTaxWithCur')
|
76 |
+
link_elem = best_match.find('productLink')
|
77 |
+
|
78 |
+
if price_elem is not None:
|
79 |
+
print(f"Price: {price_elem.text}")
|
80 |
+
if link_elem is not None:
|
81 |
+
print(f"Link: {link_elem.text}")
|
82 |
+
|
83 |
+
# Now test actual function
|
84 |
+
print("\n" + "="*60)
|
85 |
+
print("Actual function result:")
|
86 |
+
price, link = get_product_price_and_link("Marlin 5")
|
87 |
+
print(f"Price: {price}")
|
88 |
+
print(f"Link: {link}")
|
89 |
+
|
90 |
+
if __name__ == "__main__":
|
91 |
+
test_with_debug()
|