86 lines
4.5 KiB
Python
86 lines
4.5 KiB
Python
from typing import Dict, List, Any
|
|
|
|
class ColumnMapper:
|
|
"""
|
|
Maps dynamic source column names (e.g., 'Item Code', 'Selling Price')
|
|
to standard system target fields (e.g., 'sku', 'price').
|
|
Provides heuristic / AI auto-mapping suggestions.
|
|
"""
|
|
|
|
STANDARD_FIELDS = {
|
|
"sku": ["sku", "item_code", "item code", "product_code", "part_number", "model_number"],
|
|
"name": ["product_name", "name", "title", "product name", "item_name"],
|
|
"parent_name": ["parent_name", "base_product", "product_group", "parent_product", "model_series", "parent_title"],
|
|
"price": ["price", "selling_price", "mrp", "unit_price", "rate"],
|
|
"cost_price": ["cost_price", "purchase_price", "buying_price", "cost price"],
|
|
"stock": ["stock", "quantity", "qty", "inventory", "stock_count", "balance"],
|
|
"brand": ["brand", "brand_name", "manufacturer", "make"],
|
|
"category": ["category", "category_name", "department", "group"],
|
|
"parent_category": ["parent_category", "parent_category_name", "parent category"],
|
|
"is_parent_feature": ["is_parent_feature", "parent_feature", "is parent feature", "featured_category"],
|
|
"parent_media_key": ["parent_media_key", "parent_image_key", "parent media key"],
|
|
"media_key": ["media_key", "media key", "image_key", "folder_key", "media_group", "variant_media_key"],
|
|
"barcode": ["barcode", "upc", "ean", "isbn"],
|
|
"description": ["description", "details", "specifications", "summary", "product_description"],
|
|
"seo_title": ["seo_title", "seo title", "meta_title", "page_title", "seo_name"],
|
|
"seo_description": ["seo_description", "seo description", "meta_description", "seo_desc"],
|
|
"meta_keywords": ["meta_keywords", "meta keywords", "seo_keywords", "keywords"],
|
|
"device_series": ["device_series", "series", "device series", "series_name"],
|
|
"device_model": ["device_model", "model", "device model", "target_model", "compatibility_model"],
|
|
"device_type": ["device_type", "device type", "type", "device_category"],
|
|
"warranty_type": ["warranty_type", "warranty type", "warranty_mode", "guarantee_type"],
|
|
"warranty_summary": ["warranty_summary", "warranty summary", "warranty_details", "warranty_desc", "warranty_info", "guarantee_details"]
|
|
}
|
|
|
|
@classmethod
|
|
def suggest_mappings(cls, headers: List[str]) -> Dict[str, str]:
|
|
"""
|
|
Suggests mapping of source header -> standard target field.
|
|
"""
|
|
suggestions = {}
|
|
for header in headers:
|
|
normalized = header.lower().strip().replace("-", "_")
|
|
matched = False
|
|
for target_field, aliases in cls.STANDARD_FIELDS.items():
|
|
if normalized == target_field or normalized in aliases:
|
|
suggestions[header] = target_field
|
|
matched = True
|
|
break
|
|
if not matched:
|
|
# Fuzzy keyword match fallback
|
|
for target_field, aliases in cls.STANDARD_FIELDS.items():
|
|
for alias in aliases:
|
|
if alias in normalized:
|
|
# Avoid matching 'rate' in non-price contexts like 'refresh_rate' or 'heart_rate'
|
|
if alias == 'rate' and ('refresh' in normalized or 'heart' in normalized):
|
|
continue
|
|
suggestions[header] = target_field
|
|
matched = True
|
|
break
|
|
if matched:
|
|
break
|
|
if not matched:
|
|
suggestions[header] = header # Keep as custom dynamic attribute
|
|
return suggestions
|
|
|
|
@classmethod
|
|
def apply_mapping(cls, raw_row: Dict[str, Any], column_maps: Dict[str, str]) -> Dict[str, Any]:
|
|
"""
|
|
Transforms a raw row using the given column_maps dictionary.
|
|
Auto-generates mapping suggestions if column_maps is empty or missing.
|
|
"""
|
|
if not column_maps or column_maps == {}:
|
|
column_maps = cls.suggest_mappings(list(raw_row.keys()))
|
|
|
|
mapped_row = {}
|
|
attributes = {}
|
|
for source_col, val in raw_row.items():
|
|
target_field = column_maps.get(source_col, source_col)
|
|
if target_field in cls.STANDARD_FIELDS:
|
|
mapped_row[target_field] = val
|
|
else:
|
|
attributes[target_field] = val
|
|
|
|
if attributes:
|
|
mapped_row["attributes"] = attributes
|
|
return mapped_row
|