Files
fondex-store-service/sync_firebase_products.py

200 lines
6.6 KiB
Python

"""
Firebase → Django mahsulotlarni sinxronlashtirish skripti.
Ishlatish:
python sync_firebase_products.py
python sync_firebase_products.py --dry-run # haqiqatda saqlamaydi
python sync_firebase_products.py --update # mavjudlarni ham yangilaydi
Logika:
- firestore_id bo'yicha tekshiradi
- Mavjud bo'lsa — o'tkazib ketadi (--update bilan yangilaydi)
- Yangi bo'lsa — create qiladi
"""
import os
import sys
import argparse
import django
import firebase_admin
from firebase_admin import credentials, firestore
from decimal import Decimal, InvalidOperation
# ── Django sozlash ───────────────────────────────────────────────────────────
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, BASE_DIR)
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.local")
try:
django.setup()
except Exception as e:
print(f"[XATO] Django setup: {e}")
sys.exit(1)
from core.apps.vendors.models import (
VendorproductModel,
CategoryModel,
SectionModel,
)
# ── Firebase ulanish ─────────────────────────────────────────────────────────
CERT_PATH = os.path.join(BASE_DIR, "fondexuzb-firebase-adminsdk-fbsvc-7b0e2d6200.json")
if not os.path.exists(CERT_PATH):
print(f"[XATO] Firebase credentials topilmadi: {CERT_PATH}")
sys.exit(1)
if not firebase_admin._apps:
cred = credentials.Certificate(CERT_PATH)
firebase_admin.initialize_app(cred)
db = firestore.client()
# ── Yordamchi funksiyalar ────────────────────────────────────────────────────
def to_decimal(value, default=Decimal("0")):
"""Har qanday qiymatni Decimal'ga o'tkazadi."""
try:
return Decimal(str(value)).quantize(Decimal("0.01"))
except (InvalidOperation, TypeError):
return default
def to_int(value, default=-1):
try:
return int(value)
except (TypeError, ValueError):
return default
def get_category(firestore_id: str):
"""CategoryModel'ni firestore_id bo'yicha topadi."""
if not firestore_id:
return None
return CategoryModel.objects.filter(firestore_id=firestore_id).first()
def get_section(firestore_id: str):
"""SectionModel'ni firestore_id bo'yicha topadi."""
if not firestore_id:
return None
return SectionModel.objects.filter(firestore_id=firestore_id).first()
def build_product_data(doc_id: str, data: dict) -> dict:
"""Firebase hujjatidan Django model uchun dict yasaydi."""
# product_specification: {"Og'irlik": "1 kg", "Hajm": "500 ml"} kabi
spec = data.get("product_specification")
if isinstance(spec, str):
# Ba'zida JSON string sifatida keladi
import json
try:
spec = json.loads(spec)
except Exception:
spec = None
if not isinstance(spec, dict) or not spec:
spec = None
return dict(
firestore_id=doc_id,
vendor=data.get("vendorID") or data.get("vendor") or "",
name=(data.get("name") or "").strip() or "",
description=data.get("description") or "",
price=to_decimal(data.get("price", 0)),
discount_price=to_decimal(data.get("disPrice", 0)),
quantity=to_int(data.get("quantity", -1)),
is_publish=bool(data.get("publish", True)),
photos_json=data.get("photos") or None,
product_specification=spec,
# ForeignKey'lar ayrida to'ldiriladi
_category_id=data.get("categoryID") or "",
_section_id=data.get("section_id") or "",
)
def sync_products(dry_run: bool = False, update: bool = False):
print("Firebase vendor_products kolleksiyasi o'qilmoqda…")
docs = list(db.collection("vendor_products").stream())
print(f"Jami Firebase'da: {len(docs)} ta mahsulot\n")
created = 0
updated = 0
skipped = 0
errors = 0
for doc in docs:
doc_id = doc.id
data = doc.to_dict() or {}
if not data.get("name"):
print(f" [O'TKAZILDI] {doc_id} — nomi yo'q")
skipped += 1
continue
try:
product_data = build_product_data(doc_id, data)
category_fid = product_data.pop("_category_id")
section_fid = product_data.pop("_section_id")
exists = VendorproductModel.objects.filter(firestore_id=doc_id).first()
if exists and not update:
skipped += 1
continue
# ForeignKey topish
category = get_category(category_fid)
section = get_section(section_fid)
if exists and update:
for field, val in product_data.items():
setattr(exists, field, val)
exists.category = category
exists.section = section
if not dry_run:
exists.save()
print(f" [YANGILANDI] {doc_id}{product_data['name']}")
updated += 1
else:
if not dry_run:
VendorproductModel.objects.create(
**product_data,
category=category,
section=section,
)
print(f" [YARATILDI] {doc_id}{product_data['name']}")
created += 1
except Exception as e:
print(f" [XATO] {doc_id}: {e}")
errors += 1
print("\n" + "=" * 50)
print(f"Yaratildi : {created}")
print(f"Yangilandi: {updated}")
print(f"O'tkazildi: {skipped}")
print(f"Xatolar : {errors}")
if dry_run:
print("\n[DRY-RUN] Hech narsa saqlanmadi.")
# ── Entry point ───────────────────────────────────────────────────────────────
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Firebase → Django mahsulot sync")
parser.add_argument(
"--dry-run",
action="store_true",
help="Haqiqatda DB'ga yozmaydi, faqat natijalari ko'rsatadi",
)
parser.add_argument(
"--update",
action="store_true",
help="Mavjud mahsulotlarni ham yangilaydi (default: o'tkazib ketadi)",
)
args = parser.parse_args()
sync_products(dry_run=args.dry_run, update=args.update)