#!/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()