|
|
@@ -1730,10 +1730,69 @@ class DownloadProductsWithAttributesExcelView(APIView):
|
|
|
return response
|
|
|
|
|
|
|
|
|
+# class ProductAttributesUploadView(APIView):
|
|
|
+# """
|
|
|
+# POST API to upload an Excel file and add mandatory/additional attributes
|
|
|
+# for product types with possible values.
|
|
|
+# """
|
|
|
+# parser_classes = (MultiPartParser, FormParser)
|
|
|
+
|
|
|
+# def post(self, request):
|
|
|
+# file_obj = request.FILES.get('file')
|
|
|
+# if not file_obj:
|
|
|
+# return Response({"error": "No file provided."}, status=status.HTTP_400_BAD_REQUEST)
|
|
|
+
|
|
|
+# try:
|
|
|
+# df = pd.read_excel(file_obj)
|
|
|
+
|
|
|
+# required_columns = {'product_type', 'attribute_name', 'is_mandatory', 'possible_values'}
|
|
|
+# if not required_columns.issubset(df.columns):
|
|
|
+# return Response({
|
|
|
+# "error": f"Missing required columns. Found: {list(df.columns)}"
|
|
|
+# }, status=status.HTTP_400_BAD_REQUEST)
|
|
|
+
|
|
|
+# for _, row in df.iterrows():
|
|
|
+# product_type_name = str(row['product_type']).strip()
|
|
|
+# attr_name = str(row['attribute_name']).strip()
|
|
|
+# is_mandatory = str(row['is_mandatory']).strip().lower() in ['yes', 'true', '1']
|
|
|
+# possible_values = str(row.get('possible_values', '')).strip()
|
|
|
+
|
|
|
+# # Get or create product type
|
|
|
+# product_type, _ = ProductType.objects.get_or_create(name=product_type_name)
|
|
|
+
|
|
|
+# # Get or create attribute
|
|
|
+# attribute, _ = ProductAttribute.objects.get_or_create(
|
|
|
+# product_type=product_type,
|
|
|
+# name=attr_name,
|
|
|
+# defaults={'is_mandatory': is_mandatory}
|
|
|
+# )
|
|
|
+# attribute.is_mandatory = is_mandatory
|
|
|
+# attribute.save()
|
|
|
+
|
|
|
+# # Handle possible values
|
|
|
+# AttributePossibleValue.objects.filter(attribute=attribute).delete()
|
|
|
+# if possible_values:
|
|
|
+# for val in [v.strip() for v in possible_values.split(',') if v.strip()]:
|
|
|
+# AttributePossibleValue.objects.create(attribute=attribute, value=val)
|
|
|
+
|
|
|
+# return Response({"message": "Attributes uploaded successfully."}, status=status.HTTP_201_CREATED)
|
|
|
+
|
|
|
+# except Exception as e:
|
|
|
+# return Response({"error": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
|
|
+
|
|
|
+
|
|
|
+from rest_framework.views import APIView
|
|
|
+from rest_framework.response import Response
|
|
|
+from rest_framework import status
|
|
|
+from rest_framework.parsers import MultiPartParser, FormParser
|
|
|
+import pandas as pd
|
|
|
+from .models import ProductType, ProductAttribute, AttributePossibleValue
|
|
|
+
|
|
|
+
|
|
|
class ProductAttributesUploadView(APIView):
|
|
|
"""
|
|
|
- POST API to upload an Excel file and add mandatory/additional attributes
|
|
|
- for product types with possible values.
|
|
|
+ POST API to upload an Excel file and synchronize attributes for each product type.
|
|
|
+ Adds, updates, and deletes attributes and possible values.
|
|
|
"""
|
|
|
parser_classes = (MultiPartParser, FormParser)
|
|
|
|
|
|
@@ -1751,36 +1810,68 @@ class ProductAttributesUploadView(APIView):
|
|
|
"error": f"Missing required columns. Found: {list(df.columns)}"
|
|
|
}, status=status.HTTP_400_BAD_REQUEST)
|
|
|
|
|
|
- for _, row in df.iterrows():
|
|
|
- product_type_name = str(row['product_type']).strip()
|
|
|
- attr_name = str(row['attribute_name']).strip()
|
|
|
- is_mandatory = str(row['is_mandatory']).strip().lower() in ['yes', 'true', '1']
|
|
|
- possible_values = str(row.get('possible_values', '')).strip()
|
|
|
+ # Normalize data
|
|
|
+ df['product_type'] = df['product_type'].astype(str).str.strip()
|
|
|
+ df['attribute_name'] = df['attribute_name'].astype(str).str.strip()
|
|
|
|
|
|
- # Get or create product type
|
|
|
+ # Track processed attributes per product type
|
|
|
+ processed_attrs = {}
|
|
|
+
|
|
|
+ # Group by product type for deletion logic
|
|
|
+ for product_type_name, group_df in df.groupby('product_type'):
|
|
|
product_type, _ = ProductType.objects.get_or_create(name=product_type_name)
|
|
|
|
|
|
- # Get or create attribute
|
|
|
- attribute, _ = ProductAttribute.objects.get_or_create(
|
|
|
- product_type=product_type,
|
|
|
- name=attr_name,
|
|
|
- defaults={'is_mandatory': is_mandatory}
|
|
|
+ uploaded_attrs = set(group_df['attribute_name'].tolist())
|
|
|
+
|
|
|
+ # --- 🧹 Delete attributes that are NOT in the uploaded file ---
|
|
|
+ existing_attrs = set(
|
|
|
+ ProductAttribute.objects.filter(product_type=product_type)
|
|
|
+ .values_list('name', flat=True)
|
|
|
)
|
|
|
- attribute.is_mandatory = is_mandatory
|
|
|
- attribute.save()
|
|
|
+ to_delete = existing_attrs - uploaded_attrs
|
|
|
+ if to_delete:
|
|
|
+ ProductAttribute.objects.filter(
|
|
|
+ product_type=product_type,
|
|
|
+ name__in=to_delete
|
|
|
+ ).delete()
|
|
|
|
|
|
- # Handle possible values
|
|
|
- AttributePossibleValue.objects.filter(attribute=attribute).delete()
|
|
|
- if possible_values:
|
|
|
- for val in [v.strip() for v in possible_values.split(',') if v.strip()]:
|
|
|
- AttributePossibleValue.objects.create(attribute=attribute, value=val)
|
|
|
+ # --- 🆕 Create / Update remaining attributes ---
|
|
|
+ for _, row in group_df.iterrows():
|
|
|
+ attr_name = str(row['attribute_name']).strip()
|
|
|
+ is_mandatory = str(row['is_mandatory']).strip().lower() in ['yes', 'true', '1']
|
|
|
+ possible_values = str(row.get('possible_values', '')).strip()
|
|
|
+
|
|
|
+ attribute, _ = ProductAttribute.objects.get_or_create(
|
|
|
+ product_type=product_type,
|
|
|
+ name=attr_name,
|
|
|
+ defaults={'is_mandatory': is_mandatory}
|
|
|
+ )
|
|
|
+
|
|
|
+ # Update mandatory flag
|
|
|
+ attribute.is_mandatory = is_mandatory
|
|
|
+ attribute.save()
|
|
|
+
|
|
|
+ # Replace possible values
|
|
|
+ AttributePossibleValue.objects.filter(attribute=attribute).delete()
|
|
|
+ if possible_values:
|
|
|
+ for val in [v.strip() for v in possible_values.split(',') if v.strip()]:
|
|
|
+ AttributePossibleValue.objects.create(attribute=attribute, value=val)
|
|
|
|
|
|
- return Response({"message": "Attributes uploaded successfully."}, status=status.HTTP_201_CREATED)
|
|
|
+ processed_attrs[product_type_name] = {
|
|
|
+ "uploaded": list(uploaded_attrs),
|
|
|
+ "deleted": list(to_delete)
|
|
|
+ }
|
|
|
+
|
|
|
+ return Response({
|
|
|
+ "message": "Attributes synchronized successfully.",
|
|
|
+ "summary": processed_attrs
|
|
|
+ }, status=status.HTTP_201_CREATED)
|
|
|
|
|
|
except Exception as e:
|
|
|
return Response({"error": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
|
|
|
|
|
|
|
|
+
|
|
|
class ProductTypeAttributesView(APIView):
|
|
|
"""
|
|
|
API to view, create, update, and delete product type attributes and their possible values.
|