160 lines
4.8 KiB
Python
160 lines
4.8 KiB
Python
#!/usr/bin/env python3
|
|
"""Seed the sample supplier, import its catalog, price + publish valid products.
|
|
|
|
Idempotent: safe to re-run — supplier/catalog are upserted, prices recalculated.
|
|
|
|
Usage: python3 scripts/seed.py
|
|
"""
|
|
import os
|
|
import sys
|
|
|
|
# allow running from repo root or scripts/
|
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
|
|
from decimal import ROUND_HALF_UP, Decimal
|
|
|
|
from app.database import SessionLocal
|
|
from app.engines.importer import import_feed
|
|
from app.engines.pricing import calculate_price
|
|
from app.engines.suppliers.base import build_adapter
|
|
from app.models import (
|
|
AuditLog,
|
|
PriceHistory,
|
|
Supplier,
|
|
SupplierPerformance,
|
|
SupplierProduct,
|
|
)
|
|
|
|
SAMPLE_NAME = "Sample Home & Gadgets"
|
|
|
|
|
|
def ensure_supplier(db):
|
|
supplier = db.query(Supplier).filter(Supplier.name == SAMPLE_NAME).first()
|
|
if supplier is None:
|
|
supplier = Supplier(
|
|
name=SAMPLE_NAME,
|
|
adapter_type="sample",
|
|
shipping_regions=["US", "CA"],
|
|
reliability_score=92.0,
|
|
status="active",
|
|
)
|
|
db.add(supplier)
|
|
db.flush()
|
|
print(f"[seed] created supplier {supplier.id}")
|
|
else:
|
|
supplier.status = "active"
|
|
print(f"[seed] supplier already exists: {supplier.id}")
|
|
return supplier
|
|
|
|
|
|
def ensure_performance(db, supplier):
|
|
if db.query(SupplierPerformance).filter(SupplierPerformance.supplier_id == supplier.id).first() is None:
|
|
db.add(
|
|
SupplierPerformance(
|
|
supplier_id=supplier.id,
|
|
fulfillment_rate=98.0,
|
|
avg_shipping_days=4.0,
|
|
cancellation_rate=1.0,
|
|
stock_accuracy=97.0,
|
|
return_rate=2.0,
|
|
)
|
|
)
|
|
db.flush()
|
|
|
|
|
|
def price_and_publish(db, supplier):
|
|
"""Price every linked product via the pricing engine; publish valid ones.
|
|
|
|
NEVER publish invalid: products that fail rule lookup or end up with a
|
|
non-positive retail price stay un-published.
|
|
"""
|
|
links = (
|
|
db.query(SupplierProduct)
|
|
.filter(SupplierProduct.supplier_id == supplier.id)
|
|
.all()
|
|
)
|
|
published = 0
|
|
skipped = []
|
|
for sp in links:
|
|
product = sp.product
|
|
if product is None:
|
|
continue
|
|
quote = calculate_price(
|
|
db,
|
|
product.cost,
|
|
shipping=sp.shipping_cost or 0,
|
|
fees=0,
|
|
min_price=product.min_price,
|
|
)
|
|
if quote is None:
|
|
skipped.append({"sku": product.sku, "reason": "no active price rule"})
|
|
continue
|
|
if quote["retail"] <= 0:
|
|
skipped.append({"sku": product.sku, "reason": "non-positive retail"})
|
|
continue
|
|
|
|
product.retail_price = quote["retail"]
|
|
product.min_price = (quote["retail"] * Decimal("0.90")).quantize(
|
|
Decimal("0.01"), rounding=ROUND_HALF_UP
|
|
)
|
|
product.max_price = (quote["retail"] * Decimal("1.15")).quantize(
|
|
Decimal("0.01"), rounding=ROUND_HALF_UP
|
|
)
|
|
product.status = "price_calculated"
|
|
db.add(PriceHistory(product_id=product.id, price=quote["retail"], reason="auto"))
|
|
|
|
# published only when priced
|
|
product.status = "published"
|
|
published += 1
|
|
db.add(
|
|
AuditLog(
|
|
actor="seed",
|
|
action="product_published",
|
|
entity="product",
|
|
entity_id=str(product.id),
|
|
detail={"sku": product.sku, "retail_price": float(quote["retail"])},
|
|
)
|
|
)
|
|
|
|
db.commit()
|
|
print(f"[seed] priced + published {published} products; skipped: {skipped}")
|
|
|
|
|
|
def main():
|
|
db = SessionLocal()
|
|
try:
|
|
supplier = ensure_supplier(db)
|
|
db.commit()
|
|
ensure_performance(db, supplier)
|
|
db.commit()
|
|
|
|
summary = import_feed(db, supplier, feed_type="sample", data_or_url="")
|
|
print(
|
|
f"[seed] import: received={summary['received']} "
|
|
f"imported={summary['imported']} updated={summary['updated']} "
|
|
f"skipped={len(summary['skipped'])}"
|
|
)
|
|
if summary["skipped"]:
|
|
print(f"[seed] skipped details: {summary['skipped']}")
|
|
|
|
price_and_publish(db, supplier)
|
|
|
|
# quick self-check on a known item ($80-ish cost band)
|
|
probe = db.query(SupplierProduct).filter(
|
|
SupplierProduct.supplier_id == supplier.id,
|
|
SupplierProduct.supplier_sku == "SPL-1006",
|
|
).first()
|
|
if probe and probe.product:
|
|
p = probe.product
|
|
print(
|
|
f"[seed] sanity: {p.sku} cost={p.cost} retail={p.retail_price} "
|
|
f"status={p.status}"
|
|
)
|
|
print("[seed] done")
|
|
finally:
|
|
db.close()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|