345 lines
17 KiB
Python
345 lines
17 KiB
Python
import os
|
|
import zipfile
|
|
import openpyxl
|
|
from openpyxl.styles import Font, Alignment, PatternFill, Border, Side
|
|
|
|
def create_mock_png():
|
|
# 67-byte 1x1 transparent PNG file binary
|
|
return (
|
|
b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x01\x00\x00\x00\x01'
|
|
b'\x08\x06\x00\x00\x00\x1f\x15c4\x00\x00\x00\rIDATx\x9cc`\x00\x01\x00'
|
|
b'\x00\xff\xff\x03\x00\x00\x06\x00\x05\x57\xbf\xab\xcc\x00\x00\x00\x00'
|
|
b'IEND\xaeB`\x82'
|
|
)
|
|
|
|
def main():
|
|
print("Starting generation of mock migration catalog dataset...")
|
|
|
|
# Create target directories
|
|
output_dir = "migration_data"
|
|
temp_media_dir = "temp_media_folders"
|
|
os.makedirs(output_dir, exist_ok=True)
|
|
os.makedirs(temp_media_dir, exist_ok=True)
|
|
|
|
# 1. NEW CATEGORIES DEFINITION
|
|
# Final 10 categories mapping
|
|
categories_info = {
|
|
# Category Name: (Parent Category, Is Parent Feature)
|
|
"Laptop Accessories": ("", "no"),
|
|
"Laptop Batteries": ("Laptop Accessories", "no"),
|
|
"Laptop Keyboards": ("Laptop Accessories", "no"),
|
|
"Laptop Screen Protectors": ("Laptop Accessories", "no"),
|
|
"Tablets & iPads": ("", "yes"),
|
|
"Gaming Accessories": ("", "yes"),
|
|
"Refurbished Phones": ("", "yes"),
|
|
"Power Banks": ("", "no"),
|
|
"USB Cables & Adapters": ("", "no"),
|
|
"Wireless Chargers": ("", "no"),
|
|
}
|
|
|
|
# 2. BRANDS & PRODUCTS DEFINTION
|
|
# 4 Brands with multiple device types: lenovo, hp, dell, realme
|
|
brands_data = {
|
|
"Lenovo": {
|
|
"device_types": ["laptop", "tablet", "mobile"],
|
|
"products": [
|
|
# (Name, Device Type, Series, Model, Base Price, Category)
|
|
("Lenovo ThinkPad X1 Carbon", "laptop", "ThinkPad Series", "ThinkPad X1 Carbon Gen 11", 125000, "Laptop Accessories"),
|
|
("Lenovo IdeaPad Slim 5", "laptop", "IdeaPad Series", "IdeaPad Slim 5 14IRL8", 65000, "Laptop Accessories"),
|
|
("Lenovo Legion Pro 7i", "laptop", "Legion Series", "Legion Pro 7i Gen 8", 185000, "Laptop Accessories"),
|
|
("Lenovo Yoga Book 9i", "laptop", "Yoga Series", "Yoga Book 9i Dual Screen", 220000, "Laptop Accessories"),
|
|
("Lenovo Tab P12 Pro", "tablet", "Tab P Series", "Lenovo Tab P12 Pro Wi-Fi", 45000, "Tablets & iPads"),
|
|
("Lenovo Tab M10 Plus", "tablet", "Tab M Series", "Lenovo Tab M10 Plus Gen 3", 18000, "Tablets & iPads"),
|
|
("Lenovo Tab K11", "tablet", "Tab K Series", "Lenovo Tab K11 Pro LTE", 22000, "Tablets & iPads"),
|
|
("Lenovo Legion Phone Duel 2", "mobile", "Legion Phone Series", "Legion Phone Duel 2 5G", 55000, "Refurbished Phones"),
|
|
("Lenovo K15 Note", "mobile", "K-Series", "Lenovo K15 Note 4G", 12000, "Refurbished Phones"),
|
|
("Lenovo Vibe K6", "mobile", "Vibe Series", "Lenovo Vibe K6 Power", 9000, "Refurbished Phones")
|
|
]
|
|
},
|
|
"HP": {
|
|
"device_types": ["laptop", "tablet"],
|
|
"products": [
|
|
("HP Pavilion 15", "laptop", "Pavilion Series", "HP Pavilion 15-eg3000", 58000, "Laptop Accessories"),
|
|
("HP Envy x360", "laptop", "Envy Series", "HP Envy x360 15-fe0000", 82000, "Laptop Accessories"),
|
|
("HP Spectre x360", "laptop", "Spectre Series", "HP Spectre x360 14-ef0000", 145000, "Laptop Accessories"),
|
|
("HP Omen 16", "laptop", "Omen Series", "HP Omen 16-wf0000", 120000, "Laptop Accessories"),
|
|
("HP EliteBook 840", "laptop", "EliteBook Series", "HP EliteBook 840 G10", 105000, "Laptop Accessories"),
|
|
("HP Chromebook 14", "laptop", "Chromebook Series", "HP Chromebook 14a-na0000", 25000, "Laptop Accessories"),
|
|
("HP Elite x2 G8", "tablet", "Elite x2 Series", "HP Elite x2 G8 Tablet", 95000, "Tablets & iPads"),
|
|
("HP Pro Slate 12", "tablet", "Pro Slate Series", "HP Pro Slate 12 WiFi", 35000, "Tablets & iPads"),
|
|
("HP Envy x2 Tablet", "tablet", "Envy x2 Series", "HP Envy x2 12-g000", 65000, "Tablets & iPads"),
|
|
("HP Pro x2 612", "tablet", "Pro x2 Series", "HP Pro x2 612 G2", 48000, "Tablets & iPads")
|
|
]
|
|
},
|
|
"Dell": {
|
|
"device_types": ["laptop", "tablet"],
|
|
"products": [
|
|
("Dell Inspiron 15", "laptop", "Inspiron Series", "Dell Inspiron 15 3530", 52000, "Laptop Accessories"),
|
|
("Dell XPS 13 Plus", "laptop", "XPS Series", "Dell XPS 13 Plus 9320", 165000, "Laptop Accessories"),
|
|
("Dell Latitude 5440", "laptop", "Latitude Series", "Dell Latitude 5440 Business", 90000, "Laptop Accessories"),
|
|
("Dell Precision 5570", "laptop", "Precision Series", "Dell Precision 5570 Mobile Workstation", 210000, "Laptop Accessories"),
|
|
("Dell Vostro 3520", "laptop", "Vostro Series", "Dell Vostro 3520 15.6", 42000, "Laptop Accessories"),
|
|
("Dell Alienware m16", "laptop", "Alienware Series", "Alienware m16 R1 Gaming", 240000, "Laptop Accessories"),
|
|
("Dell Venue 11 Pro", "tablet", "Venue Series", "Dell Venue 11 Pro 7140", 30000, "Tablets & iPads"),
|
|
("Dell Latitude 7220 Rugged", "tablet", "Latitude Tablet Series", "Dell Latitude 7220 Rugged Extreme", 150000, "Tablets & iPads"),
|
|
("Dell XPS 12 Tablet", "tablet", "XPS Tablet Series", "Dell XPS 12 9250 2-in-1", 70000, "Tablets & iPads"),
|
|
("Dell Venue 8 Pro", "tablet", "Venue Series", "Dell Venue 8 Pro 5855", 15000, "Tablets & iPads")
|
|
]
|
|
},
|
|
"Realme": {
|
|
"device_types": ["mobile", "tablet"],
|
|
"products": [
|
|
("Realme GT 6", "mobile", "GT Series", "Realme GT 6 5G", 42000, "Refurbished Phones"),
|
|
("Realme Narzo 70 Pro", "mobile", "Narzo Series", "Realme Narzo 70 Pro 5G", 22000, "Refurbished Phones"),
|
|
("Realme C65", "mobile", "C Series", "Realme C65 5G", 11000, "Refurbished Phones"),
|
|
("Realme 12 Pro Plus", "mobile", "12 Pro Series", "Realme 12 Pro+ 5G", 32000, "Refurbished Phones"),
|
|
("Realme GT Neo 6", "mobile", "GT Series", "Realme GT Neo 6 SE", 29000, "Refurbished Phones"),
|
|
("Realme Narzo N53", "mobile", "Narzo Series", "Realme Narzo N53 4G", 9500, "Refurbished Phones"),
|
|
("Realme Pad 2", "tablet", "Pad Series", "Realme Pad 2 LTE", 20000, "Tablets & iPads"),
|
|
("Realme Pad Mini", "tablet", "Pad Mini Series", "Realme Pad Mini WiFi", 12000, "Tablets & iPads"),
|
|
("Realme Pad Slim", "tablet", "Pad Series", "Realme Pad Slim LTE", 16000, "Tablets & iPads"),
|
|
("Realme Pad Air", "tablet", "Pad Air Series", "Realme Pad Air WiFi", 18000, "Tablets & iPads")
|
|
]
|
|
}
|
|
}
|
|
|
|
# 10 Miscellaneous Category-Specific Products
|
|
category_products = [
|
|
# (Name, Brand, Device Type, Series, Model, Base Price, Category)
|
|
("Lenovo Power Bank 20000mAh", "Lenovo", "mobile", "Vibe Series", "PB-20K", 1800, "Power Banks"),
|
|
("Realme Power Bank 2", "Realme", "mobile", "C Series", "Realme PB2 10K", 1200, "Power Banks"),
|
|
("HP USB-C Fast Cable", "HP", "laptop", "Pavilion Series", "HP-CABLE-3A", 650, "USB Cables & Adapters"),
|
|
("Dell Braided USB-C Cable", "Dell", "laptop", "Inspiron Series", "Dell-BRD-5A", 850, "USB Cables & Adapters"),
|
|
("Realme Air Wireless Charger", "Realme", "mobile", "GT Series", "Realme WLS-15W", 1999, "Wireless Chargers"),
|
|
("Lenovo Smart Wireless Pad", "Lenovo", "mobile", "Edge Series", "Lenovo WLS-20W", 2499, "Wireless Chargers"),
|
|
("HP Replacement Battery Envy", "HP", "laptop", "Enens", "HP-BAT-ENVY", 4500, "Laptop Batteries"),
|
|
("Dell Replacement Battery XPS", "Dell", "laptop", "XPS Series", "Dell-BAT-XPS13", 5500, "Laptop Batteries"),
|
|
("Lenovo Stylus Pen Tab", "Lenovo", "tablet", "Tab P Series", "Lenovo Active Pen 3", 3500, "Tablets & iPads"),
|
|
("Realme Pencil Pro", "Realme", "tablet", "Pad Series", "Realme Pencil 2", 2999, "Tablets & iPads")
|
|
]
|
|
|
|
# Assemble all 50 products list
|
|
all_products = []
|
|
|
|
# 40 Brand-linked products
|
|
for brand_name, brand_meta in brands_data.items():
|
|
for p in brand_meta["products"]:
|
|
all_products.append({
|
|
"name": p[0],
|
|
"brand": brand_name,
|
|
"device_type": p[1],
|
|
"device_series": p[2],
|
|
"device_model": p[3],
|
|
"price": p[4],
|
|
"category": p[5]
|
|
})
|
|
|
|
# 10 Category-specific products
|
|
for p in category_products:
|
|
all_products.append({
|
|
"name": p[0],
|
|
"brand": p[1],
|
|
"device_type": p[2],
|
|
"device_series": p[3],
|
|
"device_model": p[4],
|
|
"price": p[5],
|
|
"category": p[6]
|
|
})
|
|
|
|
# Prepare Image ZIP package list
|
|
media_keys_to_zip = []
|
|
|
|
# 3. CONSTRUCT 200 ROWS FOR EXCEL
|
|
excel_rows = []
|
|
|
|
# Attribute variants setup
|
|
color_variants = ["Midnight Black", "Arctic White", "Slate Grey", "Ocean Blue"]
|
|
storage_variants = ["8GB/128GB", "12GB/256GB", "16GB/512GB", "16GB/1TB"]
|
|
pack_variants = ["Single Pack", "2-Pack Bundle", "3-Pack Value", "Family 5-Pack"]
|
|
|
|
for prod_idx, p in enumerate(all_products):
|
|
# Media key matching directory name in ZIP
|
|
media_key = f"{p['brand']}-{p['device_model'].replace(' ', '-').replace('/', '-')}".replace('+', '-Plus')
|
|
media_keys_to_zip.append(media_key)
|
|
|
|
# Product type flag
|
|
is_accessory = p["category"] in ("Power Banks", "USB Cables & Adapters", "Wireless Chargers", "Laptop Batteries")
|
|
|
|
# Build 4 variants for each product
|
|
for var_idx in range(4):
|
|
# Price multipliers: Arctic White = +0%, Grey = +5%, Ocean Blue = +10%, Black = +12%
|
|
# Storage multipliers: 128GB = 1.0, 256GB = 1.15, 512GB = 1.30, 1TB = 1.50
|
|
price_multiplier = 1.0
|
|
variant_name = p["name"]
|
|
sku_suffix = ""
|
|
|
|
# Attributes mapping
|
|
attr_color = color_variants[var_idx]
|
|
attr_storage = ""
|
|
attr_pack = ""
|
|
|
|
if is_accessory:
|
|
# Accessories vary by Pack Size
|
|
attr_pack = pack_variants[var_idx]
|
|
price_multiplier = [1.0, 1.8, 2.5, 3.8][var_idx]
|
|
sku_suffix = f"-PK{var_idx+1}"
|
|
variant_name = f"{p['name']} ({attr_color} - {attr_pack})"
|
|
else:
|
|
# Electronics vary by Storage/RAM
|
|
attr_storage = storage_variants[var_idx]
|
|
price_multiplier = [1.0, 1.15, 1.30, 1.55][var_idx]
|
|
sku_suffix = f"-{attr_storage.replace('/', '-')}"
|
|
variant_name = f"{p['name']} ({attr_color} - {attr_storage})"
|
|
|
|
# Final price calculations
|
|
final_price = round(p["price"] * price_multiplier, 2)
|
|
|
|
# Generate clean SKU
|
|
brand_code = p["brand"][:3].upper()
|
|
model_code = "".join([w[0] for w in p["device_model"].split() if w]).upper()[:6]
|
|
color_code = attr_color.split()[-1][:3].upper()
|
|
sku = f"{brand_code}-{model_code}-{color_code}{sku_suffix}"
|
|
|
|
# Category details
|
|
p_cat = p["category"]
|
|
parent_cat_info = categories_info.get(p_cat, ("", "no"))
|
|
parent_category = parent_cat_info[0]
|
|
is_parent_feature = parent_cat_info[1]
|
|
|
|
# Generate barcode (EAN-13 style)
|
|
barcode = f"890{prod_idx:04d}{var_idx:02d}{final_price:04.0f}"[:13]
|
|
|
|
# Generate descriptions
|
|
description = (
|
|
f"Premium {p['brand']} {p['device_model']} in elegant {attr_color}. "
|
|
f"Features cutting-edge specifications, original brand warranty, and genuine parts compatibility."
|
|
)
|
|
if attr_pack:
|
|
description += f" Packed as convenience {attr_pack}."
|
|
|
|
# Construct row dictionary
|
|
row_data = {
|
|
"sku": sku,
|
|
"name": variant_name,
|
|
"parent_name": p["name"],
|
|
"price": final_price,
|
|
"stock": 10 + (prod_idx * 3 + var_idx * 5) % 150, # dynamic inventory stock levels
|
|
"brand": p["brand"],
|
|
"category": p_cat,
|
|
"parent_category": parent_category,
|
|
"is_parent_feature": is_parent_feature,
|
|
"device_series": p["device_series"],
|
|
"device_model": p["device_model"],
|
|
"device_type": p["device_type"],
|
|
"media_key": media_key,
|
|
"barcode": barcode,
|
|
"description": description,
|
|
"Color Attribute": attr_color,
|
|
}
|
|
|
|
# Set dynamic attributes based on type
|
|
if attr_storage:
|
|
row_data["Storage Attribute"] = attr_storage
|
|
row_data["RAM Attribute"] = attr_storage.split("/")[0]
|
|
if attr_pack:
|
|
row_data["Pack Attribute"] = attr_pack
|
|
|
|
excel_rows.append(row_data)
|
|
|
|
# 4. WRITE EXCEL SHEET USING OPENPYXL
|
|
wb = openpyxl.Workbook()
|
|
ws = wb.active
|
|
ws.title = "Unified Catalog Catalog"
|
|
|
|
# Define Header list based on union of keys
|
|
headers = [
|
|
"sku", "name", "parent_name", "price", "stock", "brand", "category",
|
|
"parent_category", "is_parent_feature", "device_series", "device_model",
|
|
"device_type", "media_key", "barcode", "description", "Color Attribute",
|
|
"Storage Attribute", "RAM Attribute", "Pack Attribute"
|
|
]
|
|
|
|
# Style definitions
|
|
font_header = Font(name="Segoe UI", size=11, bold=True, color="FFFFFF")
|
|
font_data = Font(name="Segoe UI", size=10, color="000000")
|
|
fill_header = PatternFill(start_color="1F2C5C", end_color="1F2C5C", fill_type="solid") # Dark navy matching dashboard theme
|
|
fill_zebra = PatternFill(start_color="F9FAFB", end_color="F9FAFB", fill_type="solid")
|
|
|
|
thin_border = Border(
|
|
left=Side(style='thin', color='E9EDF4'),
|
|
right=Side(style='thin', color='E9EDF4'),
|
|
top=Side(style='thin', color='E9EDF4'),
|
|
bottom=Side(style='thin', color='E9EDF4')
|
|
)
|
|
|
|
# Write Headers
|
|
for col_idx, h in enumerate(headers, 1):
|
|
cell = ws.cell(row=1, column=col_idx, value=h)
|
|
cell.font = font_header
|
|
cell.fill = fill_header
|
|
cell.alignment = Alignment(horizontal="left", vertical="center")
|
|
cell.border = thin_border
|
|
|
|
# Write Rows
|
|
for row_idx, r_dict in enumerate(excel_rows, 2):
|
|
is_zebra = (row_idx % 2 == 0)
|
|
for col_idx, h in enumerate(headers, 1):
|
|
val = r_dict.get(h, "")
|
|
cell = ws.cell(row=row_idx, column=col_idx, value=val)
|
|
cell.font = font_data
|
|
cell.border = thin_border
|
|
|
|
if is_zebra:
|
|
cell.fill = fill_zebra
|
|
|
|
# Formatting and Alignment
|
|
if h in ("price", "stock"):
|
|
cell.alignment = Alignment(horizontal="right", vertical="center")
|
|
if h == "price":
|
|
cell.number_format = '₹#,##0.00'
|
|
else:
|
|
cell.number_format = '#,##0'
|
|
else:
|
|
cell.alignment = Alignment(horizontal="left", vertical="center")
|
|
|
|
# Set row heights
|
|
ws.row_dimensions[1].height = 28
|
|
for r in range(2, len(excel_rows) + 2):
|
|
ws.row_dimensions[r].height = 20
|
|
|
|
# Auto fit column widths
|
|
for col in ws.columns:
|
|
max_len = max(len(str(cell.value or '')) for cell in col)
|
|
col_letter = openpyxl.utils.get_column_letter(col[0].column)
|
|
ws.column_dimensions[col_letter].width = max(max_len + 3, 12)
|
|
|
|
# Save workbook
|
|
excel_path = os.path.join(output_dir, "catalog_import.xlsx")
|
|
wb.save(excel_path)
|
|
print(f"Created Excel import sheet at: {excel_path} (200 rows, 19 columns)")
|
|
|
|
# 5. CREATE IMAGES AND ZIP ARCHIVE
|
|
png_data = create_mock_png()
|
|
zip_path = os.path.join(output_dir, "media_images.zip")
|
|
|
|
print("Packing media images ZIP (4 mock JPEGs per product)...")
|
|
with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
|
|
for m_key in media_keys_to_zip:
|
|
# We add front, back, side, box images for each product key
|
|
for img_name in ["front.jpg", "back.jpg", "side.jpg", "box.jpg"]:
|
|
# File path in ZIP: e.g. "Lenovo-ThinkPad-X1-Carbon/front.jpg"
|
|
zip_filename = f"{m_key}/{img_name}"
|
|
# Write the 1x1 PNG data under JPEG extensions directly (browsers render valid PNGs with JPG extensions anyway)
|
|
zipf.writestr(zip_filename, png_data)
|
|
|
|
print(f"Created Media ZIP package at: {zip_path} (50 folders, 200 files)")
|
|
|
|
# Cleanup temporary folders if any
|
|
if os.path.exists(temp_media_dir):
|
|
import shutil
|
|
shutil.rmtree(temp_media_dir)
|
|
|
|
print("Successfully generated all mock migration catalog data!")
|
|
|
|
if __name__ == "__main__":
|
|
main()
|