ifixkart-backend/Backend/scripts/generate_stress_test.py

289 lines
12 KiB
Python

#!/usr/bin/env python3
"""
iFixKart Migration Stress Test Generator
=========================================
Generates increasingly large Excel + media ZIP bundles to hammer the migration engine.
Levels:
--level 1 → 500 rows, 200 images
--level 2 → 2000 rows, 800 images
--level 3 → 5000 rows, 2000 images
--level 4 → 10000 rows, 5000 images
--level 5 → 25000 rows, 10000 images
Usage:
python scripts/generate_stress_test.py --level 2 --out /tmp/stress_test
python scripts/generate_stress_test.py --all
"""
import argparse
import os
import random
import string
import struct
import zipfile
import zlib
from datetime import datetime
try:
import openpyxl
except ImportError:
print("Installing openpyxl...")
os.system("pip install openpyxl -q")
import openpyxl
# ── Domain Data ──────────────────────────────────────────────
BRANDS = ["Apple", "Samsung", "Google Pixel", "Oppo", "Vivo", "Motorola",
"Nokia", "OnePlus", "Nothing", "Asus", "Realme", "Xiaomi", "iQOO"]
PARENT_CATEGORIES = ["Accessories", "Spare Parts", "Audio", "Wearables", "Charging"]
CATEGORIES = {
"Accessories": ["Screen Guards", "Back Covers", "Mobile Cases", "Camera Lens Guards"],
"Spare Parts": ["Mobile Display", "Mobile Battery", "Mobile Camera", "Mother Board", "Mobile Back Panel"],
"Audio": ["Earbuds", "Headphones", "bluetooth speaker"],
"Wearables": ["Smart Watches"],
"Charging": ["Charging Adapters", "USB-C Cables", "Mobile Charger"],
}
DEVICE_SERIES_MAP = {
"Apple": ["iPhone 14 Series", "iPhone 15 Series", "iPhone 16 Series", "iPhone 13 Series"],
"Samsung": ["Galaxy S23 Series", "Galaxy A53 Series", "Galaxy S24 Series", "Galaxy M-Series"],
"Google Pixel": ["Pixel 7 Series", "Pixel 8 Series", "Pixel 6 Series"],
"Oppo": ["Find X6 Series", "Reno 10 Series", "A-Series"],
"Vivo": ["V27 Series", "Y-Series", "X90 Series"],
"Motorola": ["Edge 40 Series", "G-Series", "Razr Series"],
"Nokia": ["G60 Series", "X30 Series", "C-Series"],
"OnePlus": ["11 Series", "12 Series", "Nord CE Series"],
"Nothing": ["Phone 2 Series", "Phone 1 Series"],
"Asus": ["ROG Phone 7 Series", "Zenfone 10 Series"],
"Realme": ["GT5 Series", "Narzo Series", "Number Series"],
"Xiaomi": ["14 Series", "Redmi Note 13 Series", "POCO X Series"],
"iQOO": ["iQOO 11 Series", "iQOO Z7 Series"],
}
DEVICE_MODELS_MAP = {
"iPhone 14 Series": ["iPhone 14", "iPhone 14 Plus", "iPhone 14 Pro", "iPhone 14 Pro Max"],
"iPhone 15 Series": ["iPhone 15", "iPhone 15 Plus", "iPhone 15 Pro", "iPhone 15 Pro Max"],
"iPhone 16 Series": ["iPhone 16", "iPhone 16 Plus", "iPhone 16 Pro", "iPhone 16 Pro Max"],
"iPhone 13 Series": ["iPhone 13", "iPhone 13 Mini", "iPhone 13 Pro", "iPhone 13 Pro Max"],
"Galaxy S23 Series": ["Samsung Galaxy S23", "Samsung Galaxy S23+", "Samsung Galaxy S23 Ultra"],
"Galaxy S24 Series": ["Samsung Galaxy S24", "Samsung Galaxy S24+", "Samsung Galaxy S24 Ultra"],
"Galaxy A53 Series": ["Samsung Galaxy A53 5G", "Samsung Galaxy A54 5G"],
"Galaxy M-Series": ["Samsung Galaxy M34 5G", "Samsung Galaxy M54 5G"],
"Pixel 7 Series": ["Google Pixel 7", "Google Pixel 7 Pro", "Google Pixel 7a"],
"Pixel 8 Series": ["Google Pixel 8", "Google Pixel 8 Pro"],
"Pixel 6 Series": ["Google Pixel 6", "Google Pixel 6 Pro", "Google Pixel 6a"],
"Find X6 Series": ["OPPO Find X6", "OPPO Find X6 Pro"],
"Reno 10 Series": ["OPPO Reno 10", "OPPO Reno 10 Pro", "OPPO Reno 10 Pro+"],
"A-Series": ["OPPO A78 5G", "OPPO A58 5G"],
"V27 Series": ["Vivo V27", "Vivo V27 Pro"],
"Y-Series": ["Vivo Y100", "Vivo Y78 5G"],
"X90 Series": ["Vivo X90 Pro", "Vivo X90"],
"Edge 40 Series": ["Motorola Edge 40", "Motorola Edge 40 Pro"],
"G-Series": ["Motorola G84 5G", "Motorola G73 5G"],
"Razr Series": ["Motorola Razr 40 Ultra", "Motorola Razr 40"],
"G60 Series": ["Nokia G60 5G"],
"X30 Series": ["Nokia X30 5G"],
"C-Series": ["Nokia C32", "Nokia C22"],
"11 Series": ["OnePlus 11", "OnePlus 11R"],
"12 Series": ["OnePlus 12", "OnePlus 12R"],
"Nord CE Series": ["OnePlus Nord CE 3 Lite", "OnePlus Nord CE 3"],
"Phone 2 Series": ["Nothing Phone 2"],
"Phone 1 Series": ["Nothing Phone 1"],
"ROG Phone 7 Series": ["ASUS ROG Phone 7", "ASUS ROG Phone 7 Ultimate"],
"Zenfone 10 Series": ["ASUS Zenfone 10"],
"GT5 Series": ["Realme GT5", "Realme GT5 Pro"],
"Narzo Series": ["Realme Narzo 60", "Realme Narzo 60 Pro"],
"Number Series": ["Realme 11 Pro", "Realme 11 Pro+"],
"14 Series": ["Xiaomi 14", "Xiaomi 14 Pro", "Xiaomi 14 Ultra"],
"Redmi Note 13 Series": ["Redmi Note 13 5G", "Redmi Note 13 Pro", "Redmi Note 13 Pro+"],
"POCO X Series": ["POCO X5 Pro 5G", "POCO X6 Pro"],
"iQOO 11 Series": ["iQOO 11", "iQOO 11 Pro"],
"iQOO Z7 Series": ["iQOO Z7 5G", "iQOO Z7 Pro"],
}
COLORS = ["Midnight Black", "Starlight White", "Sierra Blue", "Alpine Green",
"Deep Purple", "Product Red", "Storm Grey", "Graphite",
"Natural Titanium", "White Titanium", "Black Titanium", "Blue Titanium"]
COLUMNS = [
"sku", "name", "parent_name", "brand", "category", "parent_category",
"device_series", "device_model", "price", "cost_price", "stock",
"description", "barcode", "media_key", "parent_media_key",
"Color", "Storage", "RAM", "Condition", "Weight_grams",
"Warranty_months", "In_Box_Contents", "Country_of_Origin",
]
def _make_png(width=64, height=64, seed=0):
"""Generate a valid minimal PNG using pure Python (no PIL)."""
rng = random.Random(seed)
r, g, b = rng.randint(30, 220), rng.randint(30, 220), rng.randint(30, 220)
raw_rows = []
for y in range(height):
row = bytearray([0]) # filter=None
for x in range(width):
row.extend([
max(0, min(255, r + rng.randint(-40, 40))),
max(0, min(255, g + rng.randint(-40, 40))),
max(0, min(255, b + rng.randint(-40, 40))),
])
raw_rows.append(bytes(row))
compressed = zlib.compress(b"".join(raw_rows), level=1)
def chunk(tag, data):
c = struct.pack(">I", len(data)) + tag + data
return c + struct.pack(">I", zlib.crc32(tag + data) & 0xFFFFFFFF)
png = b"\x89PNG\r\n\x1a\n"
png += chunk(b"IHDR", struct.pack(">IIBBBBB", width, height, 8, 2, 0, 0, 0))
png += chunk(b"IDAT", compressed)
png += chunk(b"IEND", b"")
return png
def _slugify(text):
return text.lower().replace(" ", "-").replace("/", "-").replace("_", "-")
def generate_rows(count):
rows = []
product_idx = 0
i = 0
while i < count:
brand = random.choice(BRANDS)
parent_cat = random.choice(PARENT_CATEGORIES)
category = random.choice(CATEGORIES[parent_cat])
series = random.choice(DEVICE_SERIES_MAP.get(brand, ["Generic Series"]))
model = random.choice(DEVICE_MODELS_MAP.get(series, ["Generic Model"]))
parent_name = f"{brand} {category} for {model}"
parent_sku_base = f"IFK-{brand[:3].upper()}-{product_idx:05d}"
parent_media_key = f"{_slugify(brand)}/{_slugify(category)}"
remaining = count - i
if remaining <= 0:
break
num_variants = random.randint(1, min(8, remaining))
for v in range(num_variants):
color = random.choice(COLORS)
storage = random.choice(["64GB", "128GB", "256GB", "512GB"])
ram = random.choice(["4GB", "6GB", "8GB", "12GB"])
condition = random.choice(["New", "Refurbished"])
price = round(random.uniform(199, 4999), 2)
cost = round(price * random.uniform(0.55, 0.80), 2)
stock = random.randint(0, 500)
barcode = "".join(random.choices(string.digits, k=13))
warranty = random.choice([6, 12, 18, 24])
rows.append({
"sku": f"{parent_sku_base}-V{v:02d}",
"name": f"{parent_name} - {color} {storage}",
"parent_name": parent_name,
"brand": brand,
"category": category,
"parent_category": parent_cat,
"device_series": series,
"device_model": model,
"price": price,
"cost_price": cost,
"stock": stock,
"description": f"Premium {category} for {model}. Color: {color}. Storage: {storage}. RAM: {ram}. Warranty: {warranty}mo.",
"barcode": barcode,
"media_key": f"{parent_media_key}/{_slugify(color)}",
"parent_media_key": parent_media_key,
"Color": color,
"Storage": storage,
"RAM": ram,
"Condition": condition,
"Weight_grams": random.randint(150, 300),
"Warranty_months": warranty,
"In_Box_Contents": "Device, Cable, Manual" if condition == "New" else "Device only",
"Country_of_Origin": random.choice(["India", "China", "Taiwan", "South Korea"]),
})
i += 1
if i >= count:
break
product_idx += 1
return rows
def build_excel(rows, out_path):
print(f" 📊 Building Excel: {len(rows):,} rows...")
wb = openpyxl.Workbook(write_only=True)
ws = wb.create_sheet("Products")
ws.append(COLUMNS)
for row in rows:
ws.append([row.get(col, "") for col in COLUMNS])
wb.save(out_path)
mb = os.path.getsize(out_path) / 1024 / 1024
print(f" ✅ Excel → {out_path} ({mb:.1f} MB)")
def build_media_zip(rows, image_count, out_path):
print(f" 🖼️ Building ZIP: {image_count:,} images...")
media_keys = list({row["media_key"] for row in rows})
random.shuffle(media_keys)
images_per_key = max(1, image_count // max(len(media_keys), 1))
generated = 0
seed = 0
with zipfile.ZipFile(out_path, "w", compression=zipfile.ZIP_DEFLATED, compresslevel=3) as zf:
for mk in media_keys:
for img_i in range(images_per_key + random.randint(0, 2)):
if generated >= image_count:
break
zf.writestr(f"{mk}/img_{img_i:04d}.png", _make_png(seed=seed))
seed += 1
generated += 1
if generated >= image_count:
break
# Top off
while generated < image_count:
mk = random.choice(media_keys)
zf.writestr(f"{mk}/x_{seed:06d}.png", _make_png(seed=seed))
seed += 1
generated += 1
mb = os.path.getsize(out_path) / 1024 / 1024
print(f" ✅ ZIP → {out_path} ({mb:.1f} MB, {generated:,} images)")
LEVELS = {
1: {"rows": 500, "images": 200, "label": "WarmUp"},
2: {"rows": 2000, "images": 800, "label": "Light"},
3: {"rows": 5000, "images": 2000, "label": "Medium"},
4: {"rows": 10000, "images": 5000, "label": "Heavy"},
5: {"rows": 25000, "images": 10000, "label": "EXTREME"},
}
def main():
parser = argparse.ArgumentParser(description="iFixKart Migration Stress Test Generator")
parser.add_argument("--level", type=int, choices=[1,2,3,4,5], default=2)
parser.add_argument("--out", type=str, default="/tmp/ifixkart_stress")
parser.add_argument("--all", dest="all_levels", action="store_true")
args = parser.parse_args()
os.makedirs(args.out, exist_ok=True)
levels_to_run = list(range(1, 6)) if args.all_levels else [args.level]
for lvl in levels_to_run:
cfg = LEVELS[lvl]
ts = datetime.now().strftime("%Y%m%d_%H%M%S")
print(f"\n{'='*60}")
print(f" 🚀 Level {lvl}{cfg['label']} ({cfg['rows']:,} rows | {cfg['images']:,} images)")
print(f"{'='*60}")
rows = generate_rows(cfg["rows"])
xlsx = os.path.join(args.out, f"stress_L{lvl}_{cfg['label']}_{ts}.xlsx")
zipp = os.path.join(args.out, f"stress_L{lvl}_{cfg['label']}_{ts}_media.zip")
build_excel(rows, xlsx)
build_media_zip(rows, cfg["images"], zipp)
print(f"\n 📦 Upload these two files together via the migration UI.")
print(f"\n✅ Done! Files in: {args.out}/\n")
if __name__ == "__main__":
main()