Files
trendyol-analiz/backend/category_seeder.py
furkanyigit34 ce1dc1e25f feat: tek birleştirilmiş JSON yapısına geçiş + sosyal kanıt fallback
Ne yaptık:
- data_consolidator.py: Tüm normalizasyon ve hesaplama mantığını main.py'den çıkardık
- Dashboard endpoint 1150 satırdan 25 satıra düştü (main.py -1730/+1880 net)
- Enrichment bitince otomatik konsolide dosya oluşturuluyor (report_{id}_data.json)
- Eski raporlar ilk dashboard isteğinde lazy migration ile konsolide ediliyor
- Trendyol API artık order-count döndürmediği için baskets fallback eklendi
- Inline socialProofs (scrape) > enrichment API öncelik sırası uygulandı
- Frontend KPI başlıkları orders/baskets durumuna göre dinamik değişiyor
- logging_config.py, category_seeder.py, alembic migration eklendi
- Playwright ile 9 tab test edildi, tüm veriler doğru

Neden yaptık:
- 3 farklı kaynaktan her istekte birleştirme yapılması veri tutarsızlığına ve yavaşlığa yol açıyordu
- Tek konsolide JSON dosyası ile dashboard anında yükleniyor
- Trendyol API değişikliği nedeniyle sipariş verisi kayboluyordu, baskets fallback ile çözüldü

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 22:25:25 +03:00

144 lines
5.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
Category Seeder - Trendyol categories JSON'dan DB'ye aktarma
Kaynak: /Users/furkanyigit/Desktop/trendyol_categories.json
3 seviye hiyerarşi: Segment (Kadın) → Grup (Giyim) → Yaprak (Elbise)
"""
import json
import re
import os
from database import SessionLocal, Category, Snapshot, Report, EnrichmentError
from logging_config import get_logger
log = get_logger("seeder")
DEFAULT_JSON_PATH = os.path.expanduser("~/Desktop/trendyol_categories.json")
def parse_url(url: str) -> dict:
"""URL'den path_model ve trendyol_category_id çıkar.
Örnekler:
/elbise-x-c56 → path_model="elbise-x-c56", category_id=56
/kanvas-canta-y-s20972 → path_model="kanvas-canta-y-s20972", category_id=None
/kadin-giyim-x-g1-c82 → path_model="kadin-giyim-x-g1-c82", category_id=82
"""
# Strip leading slash
path_model = url.lstrip("/")
# Try to extract -c{id} from the end
m = re.search(r"-c(\d+)$", path_model)
category_id = int(m.group(1)) if m else None
return {
"path_model": path_model,
"trendyol_category_id": category_id,
}
def seed_from_json(json_path: str = None, clear_existing: bool = True) -> dict:
"""JSON dosyasını okuyup DB'ye yazar.
Returns:
{"segments": int, "groups": int, "leaves": int, "total": int}
"""
json_path = json_path or DEFAULT_JSON_PATH
with open(json_path, "r", encoding="utf-8") as f:
data = json.load(f)
db = SessionLocal()
try:
if clear_existing:
# FK constraint nedeniyle referans veren tabloları önce temizle
db.query(EnrichmentError).delete(synchronize_session=False)
db.query(Report).delete(synchronize_session=False)
db.query(Snapshot).delete(synchronize_session=False)
db.query(Category).filter(Category.parent_id != None).delete(synchronize_session=False) # noqa: E711
db.query(Category).delete(synchronize_session=False)
db.commit()
log.info("Mevcut kategoriler ve bağlı veriler silindi")
stats = {"segments": 0, "groups": 0, "leaves": 0, "total": 0}
for segment_name, groups in data.items():
# Seviye 1: Segment (Kadın, Erkek, ...)
segment = Category(
name=segment_name,
parent_id=None,
trendyol_category_id=None,
trendyol_url=None,
path_model=None,
is_active=True,
)
db.add(segment)
db.flush() # ID'yi al
stats["segments"] += 1
stats["total"] += 1
for group_item in groups:
group_name = group_item["name"]
group_url = group_item.get("url", "")
group_parsed = parse_url(group_url) if group_url else {"path_model": None, "trendyol_category_id": None}
children = group_item.get("children", [])
if children:
# Seviye 2: Grup (Giyim, Ayakkabı, ...)
group = Category(
name=group_name,
parent_id=segment.id,
trendyol_category_id=group_parsed["trendyol_category_id"],
trendyol_url=f"https://www.trendyol.com{group_url}" if group_url else None,
path_model=group_parsed["path_model"],
is_active=True,
)
db.add(group)
db.flush()
stats["groups"] += 1
stats["total"] += 1
for leaf_item in children:
leaf_url = leaf_item.get("url", "")
leaf_parsed = parse_url(leaf_url) if leaf_url else {"path_model": None, "trendyol_category_id": None}
leaf = Category(
name=leaf_item["name"],
parent_id=group.id,
trendyol_category_id=leaf_parsed["trendyol_category_id"],
trendyol_url=f"https://www.trendyol.com{leaf_url}" if leaf_url else None,
path_model=leaf_parsed["path_model"],
is_active=True,
)
db.add(leaf)
stats["leaves"] += 1
stats["total"] += 1
else:
# Çocuğu yok — bu grup aslında yaprak
leaf = Category(
name=group_name,
parent_id=segment.id,
trendyol_category_id=group_parsed["trendyol_category_id"],
trendyol_url=f"https://www.trendyol.com{group_url}" if group_url else None,
path_model=group_parsed["path_model"],
is_active=True,
)
db.add(leaf)
stats["leaves"] += 1
stats["total"] += 1
db.commit()
log.info(f"Seed tamamlandı: {stats}")
return stats
except Exception as e:
db.rollback()
log.error(f"Seed hatası: {e}")
raise
finally:
db.close()
if __name__ == "__main__":
result = seed_from_json()
print(f"Seed tamamlandı: {result}")