From a087337239e0fe35ea03b845d2a3558f44eb8f79 Mon Sep 17 00:00:00 2001 From: furkanyigit34 <134547018+furkanyigit34@users.noreply.github.com> Date: Wed, 15 Apr 2026 01:10:59 +0300 Subject: [PATCH] =?UTF-8?q?fix:=200=20=C3=BCr=C3=BCnl=C3=BC=20kategori=205?= =?UTF-8?q?00=20hatas=C4=B1=20+=20sat=C4=B1c=C4=B1=20bilgileri=20eklendi?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- backend/data_consolidator.py | 67 ++++++++++++++++++++++++++---------- 1 file changed, 49 insertions(+), 18 deletions(-) diff --git a/backend/data_consolidator.py b/backend/data_consolidator.py index daa259f..57d53c9 100644 --- a/backend/data_consolidator.py +++ b/backend/data_consolidator.py @@ -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)