fashionistar-celery-queues / scripts /seed_vision_testdata.py
FASHIONISTAR CI/CD
🔄 Celery Queues Deploy: 4aae6106b1530c3cc7d7d64f5e3c3e1d5015691d [GitHub Actions]
27c799c
Raw
History Blame
15.3 kB
"""
FASHIONISTAR — Vision Test Data Seeder v2 (Schema-Correct)
==========================================================
Run: uv run python scripts/seed_vision_testdata.py
(from fashionistar_backend/ directory)
Schema-corrected based on actual model inspection:
• Product.title (not .name)
• Product.stock_qty (not .inventory_count)
• Product.vendor → VendorProfile FK
• Category has no .description field
• VendorProfile: use filter() not get_or_create() to avoid slug constraint
"""
import os
import sys
import decimal
from pathlib import Path
# ── Django setup ──────────────────────────────────────────────────────────────
BASE_DIR = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(BASE_DIR))
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "backend.config.development")
import django
django.setup()
# ── Imports (after django.setup) ───────────────────────────────────────────────
from django.contrib.auth import get_user_model
from django.db import transaction
UnifiedUser = get_user_model()
# ─── Credentials ──────────────────────────────────────────────────────────────
ADMIN_EMAIL = "admin@fashionistar.io"
ADMIN_PASSWORD = "FashionAdmin2026!"
VENDOR_EMAIL = "vendor.vision.2026@gmail.com"
VENDOR_PASSWORD = "VendorTest@2026!"
CLIENT_EMAIL = "client.vision.2026@gmail.com"
CLIENT_PASSWORD = "ClientTest@2026!"
# ─── Product definitions (using actual field names: title, stock_qty) ─────────
PRODUCTS = [
{
"title": "Royal Agbada Set",
"description": "Exquisite hand-stitched royal agbada set. Premium Aso-Oke fabric with intricate gold thread embroidery for grand occasions.",
"price": decimal.Decimal("85000.00"),
"stock_qty": 15,
"category_slug": "traditional",
"status": "published",
},
{
"title": "Ankara Cocktail Dress",
"description": "Vibrant Ankara cocktail dress with modern silhouette. Perfect for celebrations, dinner parties, and cultural events.",
"price": decimal.Decimal("45000.00"),
"stock_qty": 20,
"category_slug": "women",
"status": "published",
},
{
"title": "Senator Corporate Suit",
"description": "Distinguished senator suit for executive boardrooms. Tailored with premium Italian-inspired fabric and hand-finished details.",
"price": decimal.Decimal("120000.00"),
"stock_qty": 10,
"category_slug": "men",
"status": "published",
},
{
"title": "Asoebi Lace Gown",
"description": "Elegant aso-ebi lace gown for weddings and traditional ceremonies. Beautifully crafted with premium French lace.",
"price": decimal.Decimal("65000.00"),
"stock_qty": 8,
"category_slug": "women",
"status": "published",
},
{
"title": "Kids Dashiki Collection",
"description": "Vibrant kids dashiki set for cultural events and festivities. Comfortable, durable, with authentic African heritage patterns.",
"price": decimal.Decimal("22000.00"),
"stock_qty": 30,
"category_slug": "kids",
"status": "published",
},
]
CATEGORIES = [
{"name": "Traditional", "slug": "traditional"},
{"name": "Women", "slug": "women"},
{"name": "Men", "slug": "men"},
{"name": "Kids", "slug": "kids"},
]
def log(msg: str, icon: str = "→") -> None:
print(f" {icon} {msg}")
# ─── Admin ────────────────────────────────────────────────────────────────────
def seed_admin() -> object:
log("Seeding admin superuser ...", "🔑")
user, created = UnifiedUser.objects.get_or_create(
email=ADMIN_EMAIL,
defaults={
"first_name": "Fashion",
"last_name": "Admin",
"role": "admin",
"auth_provider": "email",
},
)
user.set_password(ADMIN_PASSWORD)
user.is_active = True
user.is_verified = True
user.is_staff = True
user.is_superuser = True
user.save(update_fields=["password", "is_active", "is_verified", "is_staff", "is_superuser"])
log(f"Admin {ADMIN_EMAIL}{'created' if created else 'updated'}", "✅")
return user
# ─── Vendor ───────────────────────────────────────────────────────────────────
def seed_vendor() -> object:
log("Seeding vendor user ...", "🏪")
user, created = UnifiedUser.objects.get_or_create(
email=VENDOR_EMAIL,
defaults={
"first_name": "TestVendor",
"last_name": "Vision2026",
"role": "vendor",
"auth_provider": "email",
},
)
user.set_password(VENDOR_PASSWORD)
user.is_active = True
user.is_verified = True
user.save(update_fields=["password", "is_active", "is_verified"])
log(f"Vendor {VENDOR_EMAIL}{'created' if created else 'updated'}", "✅")
# VendorProfile — use filter to avoid slug constraint
try:
from apps.vendor.models import VendorProfile
profile = VendorProfile.objects.filter(user=user).first()
if profile is None:
profile = VendorProfile(user=user)
if not getattr(profile, "store_name", None):
profile.store_name = "Adaeze Couture"
profile.city = "Lagos"
profile.state = "Lagos"
profile.country = "Nigeria"
profile.address = "10 Kingsway Road"
for field in ["is_active", "is_verified"]:
if hasattr(profile, field):
setattr(profile, field, True)
for field, value in [
("account_name", "Adaeze Vision"),
("bank_name", "Zenith Bank"),
("account_number", "1012345678"),
("description", "Custom African luxury styles and designs."),
]:
if hasattr(profile, field):
setattr(profile, field, value)
profile.save()
log("VendorProfile 'Adaeze Couture' saved", "✅")
except Exception as e:
log(f"VendorProfile skip: {e}", "⚠️")
return user
# ─── Client ───────────────────────────────────────────────────────────────────
def seed_client() -> object:
log("Seeding client user ...", "👤")
user, created = UnifiedUser.objects.get_or_create(
email=CLIENT_EMAIL,
defaults={
"first_name": "TestClient",
"last_name": "Vision2026",
"role": "client",
"auth_provider": "email",
},
)
user.set_password(CLIENT_PASSWORD)
user.is_active = True
user.is_verified = True
user.save(update_fields=["password", "is_active", "is_verified"])
log(f"Client {CLIENT_EMAIL}{'created' if created else 'updated'}", "✅")
try:
from apps.client.models import ClientProfile
if hasattr(ClientProfile, "get_or_create_for_user"):
profile = ClientProfile.get_or_create_for_user(user)
else:
profile, _ = ClientProfile.objects.get_or_create(user=user)
profile.default_shipping_address = "10 Kingsway Road, Ikoyi, Lagos"
profile.state = "Lagos"
profile.country = "Nigeria"
if hasattr(profile, "is_profile_complete"):
profile.is_profile_complete = True
profile.save()
log("ClientProfile saved", "✅")
except Exception as e:
log(f"ClientProfile skip: {e}", "⚠️")
return user
# ─── Categories ───────────────────────────────────────────────────────────────
def seed_categories() -> dict:
log("Seeding catalog categories ...", "📂")
category_map = {}
try:
from apps.catalog.models import Category
# Inspect actual model fields
actual_fields = {f.name for f in Category._meta.get_fields() if hasattr(f, "name")}
log(f"Category fields: {sorted(actual_fields)}", " ℹ")
for cat_data in CATEGORIES:
# Try by slug first
cat = Category.objects.filter(slug=cat_data["slug"]).first()
if not cat:
cat = Category.objects.filter(name=cat_data["name"]).first()
if not cat:
kwargs: dict = {"name": cat_data["name"]}
if "slug" in actual_fields:
kwargs["slug"] = cat_data["slug"]
# Explicitly activate — CatalogSelector filters active=True
if "active" in actual_fields:
kwargs["active"] = True
cat = Category.objects.create(**kwargs)
log(f"Category '{cat_data['name']}' → created (active=True)", " ✅")
else:
# Ensure existing categories are also active
if hasattr(cat, "active") and not cat.active:
cat.active = True
cat.save(update_fields=["active"])
log(f"Category '{cat_data['name']}' → exists (id={cat.pk}, active={getattr(cat, 'active', '?')})", " ✅")
category_map[cat_data["slug"]] = cat
except Exception as e:
log(f"Category seeding failed: {e}", "⚠️")
return category_map
# ─── Products ─────────────────────────────────────────────────────────────────
def seed_products(vendor_user: object, category_map: dict) -> list:
log("Seeding 5 products ...", "🛍️")
seeded = []
try:
from apps.product.models import Product
except ImportError as e:
log(f"Product model import failed: {e}", "⚠️")
return seeded
# Get VendorProfile for vendor FK
vendor_profile = None
try:
from apps.vendor.models import VendorProfile
vendor_profile = VendorProfile.objects.filter(user=vendor_user).first()
if vendor_profile:
log(f"Using VendorProfile id={vendor_profile.pk}", " ℹ")
else:
log("No VendorProfile found — products will fail if vendor FK is required", " ⚠️")
except Exception as e:
log(f"VendorProfile lookup failed: {e}", " ⚠️")
# Inspect Product model fields
actual_fields = {f.name for f in Product._meta.get_fields() if hasattr(f, "name")}
log(f"Product title field: {'title' if 'title' in actual_fields else 'name' if 'name' in actual_fields else 'UNKNOWN'}", " ℹ")
title_field = "title" if "title" in actual_fields else "name"
inv_field = "stock_qty" if "stock_qty" in actual_fields else "inventory_count" if "inventory_count" in actual_fields else None
for i, pd in enumerate(PRODUCTS, start=1):
log(f"[{i}/5] '{pd['title']}' ...", " →")
try:
create_kwargs: dict = {
title_field: pd["title"],
"description": pd["description"],
"price": pd["price"],
"status": pd["status"],
}
if inv_field:
create_kwargs[inv_field] = pd["stock_qty"]
if vendor_profile:
create_kwargs["vendor"] = vendor_profile
else:
log(" No VendorProfile — skipping remaining products", " ⚠️")
break
# Category FK
cat = category_map.get(pd["category_slug"])
if cat and "categories" in actual_fields:
# M2M — handle after create
pass
elif cat and "category" in actual_fields:
create_kwargs["category"] = cat
# Upsert by title + vendor
existing = Product.objects.filter(
**{title_field: pd["title"], "vendor": vendor_profile}
).first()
if existing:
for k, v in create_kwargs.items():
setattr(existing, k, v)
existing.save()
p = existing
log(f" '{pd['title']}' ₦{pd['price']} → updated", " ✅")
else:
p = Product.objects.create(**create_kwargs)
log(f" '{pd['title']}' ₦{pd['price']} → created", " ✅")
# Assign M2M category if applicable
if cat and "categories" in actual_fields and hasattr(p, "categories"):
try:
p.categories.add(cat)
log(f" Category '{cat.name}' added to product", " ✅")
except Exception as e_cat:
log(f" Category M2M skip: {e_cat}", " ⚠️")
seeded.append(p)
except Exception as e:
log(f" '{pd['title']}' FAILED: {e}", " ❌")
log(f"Products seeded: {len(seeded)}/5", "📊")
return seeded
# ─── Main ─────────────────────────────────────────────────────────────────────
@transaction.atomic
def run_seed():
print("=" * 60)
print(" 🚀 FASHIONISTAR — Vision Test Data Seeder v2")
print(f" 📅 2026-05-28 | Env: {os.environ.get('DJANGO_SETTINGS_MODULE')}")
print("=" * 60)
admin_user = seed_admin()
vendor_user = seed_vendor()
client_user = seed_client()
categories = seed_categories()
products = seed_products(vendor_user, categories)
print("\n" + "=" * 60)
print(" 📊 SEED SUMMARY")
print("=" * 60)
print(f" ✅ Admin : {ADMIN_EMAIL}")
print(f" ✅ Vendor : {VENDOR_EMAIL}")
print(f" ✅ Client : {CLIENT_EMAIL}")
print(f" ✅ Categories : {len(categories)}")
print(f" ✅ Products : {len(products)}/5")
print("=" * 60)
print()
if len(products) < 5:
print(" ⚠️ Product seeding incomplete — check errors above.")
print(" ℹ You can also seed manually via the vendor dashboard:")
print(f" {os.environ.get('NEXT_PUBLIC_FRONTEND_TUNNEL_URL', 'http://localhost:3000')}/vendor-dashboard/product/create")
print()
print(" ℹ Run the Playwright suite next:")
print(" cd fashionista_frontend")
print(" $env:PLAYWRIGHT_SKIP_WEB_SERVER=1")
print(" pnpm exec playwright test tests/e2e/master-vision-e2e-2026-05-28.spec.ts --project=\"chromium — Desktop\" --reporter=list")
print()
if __name__ == "__main__":
run_seed()