fix: 0 ürünlü kategori 500 hatası + satıcı bilgileri eklendi

Ne yaptık:
- build_consolidated_report(): normalized_products boş olunca
  None yerine empty=true içeren boş dashboard döndürüyor
- normalize_product(): seller_count, has_buybox, is_trendyol_product alanları eklendi
- Social proof: metrik bazında en doğru kaynak seçimi (inline vs enrichment API)

Neden yaptık:
- Boş kategorilerde dashboard-data endpoint 500 fırlatıyordu,
  kullanıcı loading skeleton'dan çıkamıyordu
- Satıcı sayısı ve buybox bilgisi Ürünler tablosu için gerekli

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
furkanyigit34
2026-04-15 01:10:59 +03:00
parent 8cbe100035
commit a087337239

View File

@@ -177,39 +177,43 @@ def normalize_product(raw_product, category_name, social_details):
brand = raw_product.get("brand", {})
brand_name = (brand.get("name") if isinstance(brand, dict) else brand) or "Bilinmeyen"
# ── Social proof: önce inline socialProofs, sonra enrichment ──
# ── Social proof: metrik bazında en doğru kaynağı seç ──
orders, page_views, baskets, favorites = 0, 0, 0, 0
# İnline socialProofs (Top Rankings API — ürün dosyasında kayıtlı)
# 1. Inline socialProofs (Top Rankings API)sipariş için kesin sayı verir
inline_orders, inline_views, inline_baskets, inline_favorites = 0, 0, 0, 0
social_proofs = raw_product.get("socialProofs", [])
if isinstance(social_proofs, list):
for proof in social_proofs:
proof_type = proof.get("type", "")
parsed = _parse_social_value(proof.get("value", "0"))
if proof_type == "orderCountL3D":
orders = parsed
inline_orders = parsed
elif proof_type == "pageViewCount":
page_views = parsed
inline_views = parsed
elif proof_type == "basketCount":
baskets = parsed
inline_baskets = parsed
elif proof_type == "favoriteCount":
favorites = parsed
inline_favorites = parsed
# Enrichment API (social.json) — inline yoksa veya 0 ise fallback
# Key hem str hem int olabilir (dosyadan str, memory'den int)
# 2. Enrichment API (social.json) — görüntülenme/sepet/favori için daha doğru
sp = {}
if product_id and social_details:
sp = (social_details.get(str(product_id))
or social_details.get(int(product_id) if str(product_id).isdigit() else -1)
or {})
if not orders:
orders = sp.get("orders", 0) or 0
if not page_views:
page_views = sp.get("page_views", 0) or 0
if not baskets:
baskets = sp.get("baskets", 0) or 0
if not favorites:
favorites = sp.get("favorites", 0) or 0
enrich_orders = sp.get("orders", 0) or 0
enrich_views = sp.get("page_views", 0) or 0
enrich_baskets = sp.get("baskets", 0) or 0
enrich_favorites = sp.get("favorites", 0) or 0
# 3. Metrik bazında en doğru kaynağı seç:
# - Sipariş: Top Rankings kesin sayı verir (294), SP API yuvarlar (500+)
# - Diğerleri: SP API daha kesin (17.8B=17800), Top Rankings yuvarlar (3k=3000)
orders = inline_orders or enrich_orders
page_views = enrich_views or inline_views
baskets = enrich_baskets or inline_baskets
favorites = enrich_favorites or inline_favorites
# ── Image URL ──
image_url = raw_product.get("imageUrl", "")
@@ -221,6 +225,8 @@ def normalize_product(raw_product, category_name, social_details):
product_url = raw_product.get("url", "")
if not product_url and product_id:
product_url = f"https://www.trendyol.com/p/{product_id}"
elif product_url and not product_url.startswith("http"):
product_url = f"https://www.trendyol.com{product_url}" if product_url.startswith("/") else f"https://www.trendyol.com/{product_url}"
# ── Barcode ──
barcode = ""
@@ -242,6 +248,11 @@ def normalize_product(raw_product, category_name, social_details):
)
break
# ── Satıcı bilgileri ──
seller_count = len(merchant_listings)
has_buybox = merchant_listings[0].get("isWinner", False) if merchant_listings else False
is_trendyol_product = barcode.startswith(("TYB", "SGT", "KPE", "RTN", "CDM")) if barcode else False
return {
"id": product_id,
"name": raw_product.get("name", ""),
@@ -261,6 +272,9 @@ def normalize_product(raw_product, category_name, social_details):
"image_url": image_url or "https://via.placeholder.com/150",
"url": product_url,
"in_stock": raw_product.get("inStock", False),
"seller_count": seller_count,
"has_buybox": has_buybox,
"is_trendyol_product": is_trendyol_product,
}
@@ -732,8 +746,25 @@ def build_consolidated_report(report_id, db, reports_dir, social_data=None):
continue
if not normalized_products:
log.warning(f"Rapor {report_id} için ürün bulunamadı")
return None
log.warning(f"Rapor {report_id} için ürün bulunamadı — boş dashboard döndürülüyor")
return {
"metadata": {
"report_id": report_id,
"report_name": report.name,
"created_at": report.created_at.isoformat() if report.created_at else None,
"total_products": 0,
"total_categories": 0,
"consolidated_at": datetime.now().isoformat(),
"empty": True,
},
"report_id": report_id,
"report_name": report.name,
"products": [],
"all_products": [],
"kpis": calculate_kpis([]),
"charts": calculate_charts([]),
"insights": calculate_insights([]),
}
# ── Hesaplamalar ──
kpis = calculate_kpis(normalized_products)