123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556 |
- # # views.py (Enhanced)
- # from django.shortcuts import render, get_object_or_404
- # from django.http import JsonResponse
- # from django.views import View
- # from django.core.cache import cache
- # import json
- # import logging
- # from core.models import AttributeScore, CategoryAttributeRule, Product
- # from core.services.attribute_scorer import AttributeQualityScorer
- # from django.views.decorators.csrf import csrf_exempt
- # from django.utils.decorators import method_decorator
- # logger = logging.getLogger(__name__)
- # @method_decorator(csrf_exempt, name='dispatch')
- # class AttributeScoreView(View):
- # """Enhanced API view with caching and AI suggestions"""
- # def __init__(self, *args, **kwargs):
- # super().__init__(*args, **kwargs)
- # self.scorer = AttributeQualityScorer(use_ai=True) # enable AI
- # def post(self, request, *args, **kwargs):
- # """Score a single product with AI suggestions"""
- # try:
- # data = json.loads(request.body)
- # product_data = data.get('product', {})
- # sku = product_data.get('sku')
- # use_ai = data.get('use_ai', True)
- # if not sku:
- # return JsonResponse({'error': 'SKU is required'}, status=400)
- # category = product_data.get('category', '')
- # if not category:
- # return JsonResponse({'error': 'Category is required'}, status=400)
- # # Get or create product
- # product, created = Product.objects.get_or_create(
- # sku=sku,
- # defaults={
- # 'title': product_data.get('title', ''),
- # 'description': product_data.get('description', ''),
- # 'category': category,
- # 'attributes': product_data.get('attributes', {})
- # }
- # )
- # # Update if exists
- # if not created:
- # product.title = product_data.get('title', product.title)
- # product.description = product_data.get('description', product.description)
- # product.attributes = product_data.get('attributes', product.attributes)
- # product.save()
- # # Get rules (cached)
- # cache_key = f"category_rules_{category}"
- # rules = cache.get(cache_key)
- # if rules is None:
- # rules = list(CategoryAttributeRule.objects.filter(category=category).values())
- # cache.set(cache_key, rules, 3600)
- # if not rules:
- # return JsonResponse({'error': f'No rules defined for {category}'}, status=400)
- # # Force AI suggestions
- # score_result = self.scorer.score_product(
- # {
- # 'sku': product.sku,
- # 'category': product.category,
- # 'title': product.title,
- # 'description': product.description,
- # 'attributes': product.attributes
- # },
- # rules,
- # generate_ai_suggestions=True # always generate AI
- # )
- # # Save score
- # AttributeScore.objects.create(
- # product=product,
- # score=score_result['final_score'],
- # max_score=score_result['max_score'],
- # details=score_result['breakdown'],
- # issues=score_result['issues'],
- # suggestions=score_result['suggestions'],
- # ai_suggestions=score_result.get('ai_suggestions', {}),
- # processing_time=score_result.get('processing_time', 0)
- # )
- # return JsonResponse({
- # 'success': True,
- # 'product_sku': sku,
- # 'created': created,
- # 'score_result': score_result
- # })
- # except json.JSONDecodeError:
- # return JsonResponse({'error': 'Invalid JSON'}, status=400)
- # except Exception as e:
- # logger.error(f"Error scoring product: {str(e)}", exc_info=True)
- # return JsonResponse({'error': str(e)}, status=500)
- # from django.views import View
- # from django.http import JsonResponse
- # from django.utils.decorators import method_decorator
- # from django.views.decorators.csrf import csrf_exempt
- # import json
- # import logging
- # from .models import Product, CategoryAttributeRule, AttributeScore
- # from .services.attribute_scorer import AttributeQualityScorer
- # logger = logging.getLogger(__name__)
- # @method_decorator(csrf_exempt, name='dispatch')
- # class BatchScoreView(View):
- # """Batch scoring with AI suggestions"""
- # def __init__(self, *args, **kwargs):
- # super().__init__(*args, **kwargs)
- # self.scorer = AttributeQualityScorer(use_ai=True) # enable AI even for batch
- # def post(self, request):
- # try:
- # data = json.loads(request.body)
- # products = data.get('products', [])
- # if not products:
- # return JsonResponse({'error': 'No products provided'}, status=400)
- # results = []
- # errors = []
- # for product_data in products[:100]: # limit 100
- # try:
- # sku = product_data.get('sku')
- # category = product_data.get('category')
- # if not sku or not category:
- # errors.append({'sku': sku, 'error': 'Missing SKU or category'})
- # continue
- # # Get rules
- # rules = list(CategoryAttributeRule.objects.filter(category=category).values())
- # if not rules:
- # errors.append({'sku': sku, 'error': f'No rules for category {category}'})
- # continue
- # # Force AI suggestions
- # score_result = self.scorer.score_product(
- # product_data,
- # rules,
- # generate_ai_suggestions=True # <- key change
- # )
- # results.append({
- # 'sku': sku,
- # 'final_score': score_result['final_score'],
- # 'max_score': score_result['max_score'],
- # 'breakdown': score_result['breakdown'],
- # 'issues': score_result['issues'],
- # 'suggestions': score_result['suggestions'],
- # 'ai_suggestions': score_result.get('ai_suggestions', {}),
- # 'processing_time': score_result.get('processing_time', 0)
- # })
- # except Exception as e:
- # errors.append({'sku': product_data.get('sku'), 'error': str(e)})
- # return JsonResponse({
- # 'success': True,
- # 'processed': len(results),
- # 'results': results,
- # 'errors': errors
- # })
- # except Exception as e:
- # logger.error(f"Batch scoring error: {str(e)}")
- # return JsonResponse({'error': str(e)}, status=500)
- # views.py (Enhanced with ProductContentRule support - FIXED)
- from django.shortcuts import render, get_object_or_404
- from django.http import JsonResponse
- from django.views import View
- from django.core.cache import cache
- from django.db.models import Q # ← FIXED: Import Q from django.db.models
- from django.views.decorators.csrf import csrf_exempt
- from django.utils.decorators import method_decorator
- import json
- import logging
- from core.models import AttributeScore, CategoryAttributeRule, ProductContentRule, Product
- from core.services.attribute_scorer import AttributeQualityScorer
- logger = logging.getLogger(__name__)
- @method_decorator(csrf_exempt, name='dispatch')
- class AttributeScoreView(View):
- """Enhanced API view with ProductContentRule support"""
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self.scorer = AttributeQualityScorer(use_ai=True)
- def post(self, request, *args, **kwargs):
- """Score a single product with AI suggestions and content rules validation"""
- try:
- data = json.loads(request.body)
- product_data = data.get('product', {})
- sku = product_data.get('sku')
- use_ai = data.get('use_ai', True)
- if not sku:
- return JsonResponse({'error': 'SKU is required'}, status=400)
- category = product_data.get('category', '')
- if not category:
- return JsonResponse({'error': 'Category is required'}, status=400)
- # Get or create product
- product, created = Product.objects.get_or_create(
- sku=sku,
- defaults={
- 'title': product_data.get('title', ''),
- 'description': product_data.get('description', ''),
- 'short_description': product_data.get('short_description', ''),
- 'seo_title': product_data.get('seo_title', ''),
- 'seo_description': product_data.get('seo_description', ''),
- 'category': category,
- 'attributes': product_data.get('attributes', {})
- }
- )
- # Update if exists
- if not created:
- product.title = product_data.get('title', product.title)
- product.description = product_data.get('description', product.description)
- product.short_description = product_data.get('short_description', product.short_description)
- product.seo_title = product_data.get('seo_title', product.seo_title)
- product.seo_description = product_data.get('seo_description', product.seo_description)
- product.attributes = product_data.get('attributes', product.attributes)
- product.save()
- # Get CategoryAttributeRules (cached)
- cache_key = f"category_rules_{category}"
- category_rules = cache.get(cache_key)
- if category_rules is None:
- category_rules = list(CategoryAttributeRule.objects.filter(category=category).values())
- cache.set(cache_key, category_rules, 3600)
-
- if not category_rules:
- return JsonResponse({'error': f'No attribute rules defined for {category}'}, status=400)
- # Get ProductContentRules (cached) - FIXED: Use Q from django.db.models
- content_cache_key = f"content_rules_{category}"
- content_rules = cache.get(content_cache_key)
- if content_rules is None:
- # Get both global rules (category=None) and category-specific rules
- content_rules = list(
- ProductContentRule.objects.filter(
- Q(category__isnull=True) | Q(category=category)
- ).values()
- )
- cache.set(content_cache_key, content_rules, 3600)
- # Build product dict with all fields
- product_dict = {
- 'sku': product.sku,
- 'category': product.category,
- 'title': product.title,
- 'description': product.description,
- 'short_description': product.short_description,
- 'seo_title': product.seo_title,
- 'seo_description': product.seo_description,
- 'attributes': product.attributes
- }
- # Score product with content rules
- score_result = self.scorer.score_product(
- product_dict,
- category_rules,
- content_rules=content_rules,
- generate_ai_suggestions=True
- )
- # Save score
- AttributeScore.objects.create(
- product=product,
- score=score_result['final_score'],
- max_score=score_result['max_score'],
- details=score_result['breakdown'],
- issues=score_result['issues'],
- suggestions=score_result['suggestions'],
- ai_suggestions=score_result.get('ai_suggestions', {}),
- processing_time=score_result.get('processing_time', 0)
- )
- return JsonResponse({
- 'success': True,
- 'product_sku': sku,
- 'created': created,
- 'score_result': score_result
- })
- except json.JSONDecodeError:
- return JsonResponse({'error': 'Invalid JSON'}, status=400)
- except Exception as e:
- logger.error(f"Error scoring product: {str(e)}", exc_info=True)
- return JsonResponse({'error': str(e)}, status=500)
- @method_decorator(csrf_exempt, name='dispatch')
- class BatchScoreView(View):
- """Batch scoring with AI suggestions and content rules"""
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self.scorer = AttributeQualityScorer(use_ai=True)
- def post(self, request):
- try:
- data = json.loads(request.body)
- products = data.get('products', [])
- if not products:
- return JsonResponse({'error': 'No products provided'}, status=400)
- results = []
- errors = []
- for product_data in products[:100]: # limit 100
- try:
- sku = product_data.get('sku')
- category = product_data.get('category')
- if not sku or not category:
- errors.append({'sku': sku, 'error': 'Missing SKU or category'})
- continue
- # Get attribute rules
- category_rules = list(CategoryAttributeRule.objects.filter(category=category).values())
- if not category_rules:
- errors.append({'sku': sku, 'error': f'No attribute rules for category {category}'})
- continue
- # Get content rules - FIXED: Use Q from django.db.models
- content_rules = list(
- ProductContentRule.objects.filter(
- Q(category__isnull=True) | Q(category=category)
- ).values()
- )
- # Score with content rules
- score_result = self.scorer.score_product(
- product_data,
- category_rules,
- content_rules=content_rules,
- generate_ai_suggestions=True
- )
- results.append({
- 'sku': sku,
- 'final_score': score_result['final_score'],
- 'max_score': score_result['max_score'],
- 'breakdown': score_result['breakdown'],
- 'issues': score_result['issues'],
- 'suggestions': score_result['suggestions'],
- 'ai_suggestions': score_result.get('ai_suggestions', {}),
- 'processing_time': score_result.get('processing_time', 0)
- })
- except Exception as e:
- logger.error(f"Error scoring product {product_data.get('sku')}: {str(e)}", exc_info=True)
- errors.append({'sku': product_data.get('sku'), 'error': str(e)})
- return JsonResponse({
- 'success': True,
- 'processed': len(results),
- 'results': results,
- 'errors': errors
- })
- except Exception as e:
- logger.error(f"Batch scoring error: {str(e)}", exc_info=True)
- return JsonResponse({'error': str(e)}, status=500)
- @method_decorator(csrf_exempt, name='dispatch')
- class ContentRulesView(View):
- """API to manage ProductContentRules"""
- def get(self, request):
- """Get all content rules, optionally filtered by category"""
- try:
- category = request.GET.get('category')
-
- if category:
- # FIXED: Use Q from django.db.models
- rules = ProductContentRule.objects.filter(
- Q(category__isnull=True) | Q(category=category)
- )
- else:
- rules = ProductContentRule.objects.all()
-
- rules_data = list(rules.values())
-
- return JsonResponse({
- 'success': True,
- 'count': len(rules_data),
- 'rules': rules_data
- })
-
- except Exception as e:
- logger.error(f"Error fetching content rules: {e}", exc_info=True)
- return JsonResponse({'error': str(e)}, status=500)
- def post(self, request):
- """Create a new content rule"""
- try:
- data = json.loads(request.body)
-
- required_fields = ['field_name']
- if not all(field in data for field in required_fields):
- return JsonResponse({'error': 'field_name is required'}, status=400)
-
- # Create rule
- rule = ProductContentRule.objects.create(
- category=data.get('category'),
- field_name=data['field_name'],
- is_mandatory=data.get('is_mandatory', True),
- min_length=data.get('min_length'),
- max_length=data.get('max_length'),
- min_word_count=data.get('min_word_count'),
- max_word_count=data.get('max_word_count'),
- must_contain_keywords=data.get('must_contain_keywords', []),
- validation_regex=data.get('validation_regex', ''),
- description=data.get('description', '')
- )
-
- # Clear cache
- if data.get('category'):
- cache.delete(f"content_rules_{data['category']}")
-
- return JsonResponse({
- 'success': True,
- 'rule_id': rule.id,
- 'message': 'Content rule created successfully'
- })
-
- except Exception as e:
- logger.error(f"Error creating content rule: {e}", exc_info=True)
- return JsonResponse({'error': str(e)}, status=500)
- @method_decorator(csrf_exempt, name='dispatch')
- class ProductScoreDetailView(View):
- """Get detailed score for a specific product"""
- def get(self, request, sku):
- try:
- product = get_object_or_404(Product, sku=sku)
-
- # Get latest score
- latest_score = AttributeScore.objects.filter(product=product).order_by('-created_at').first()
-
- if not latest_score:
- return JsonResponse({'error': 'No score found for this product'}, status=404)
-
- # Get interpretation
- scorer = AttributeQualityScorer()
- interpretation = scorer.get_score_interpretation(latest_score.score)
-
- return JsonResponse({
- 'success': True,
- 'product': {
- 'sku': product.sku,
- 'category': product.category,
- 'title': product.title,
- 'description': product.description,
- 'short_description': product.short_description,
- 'seo_title': product.seo_title,
- 'seo_description': product.seo_description,
- 'attributes': product.attributes
- },
- 'score': {
- 'final_score': latest_score.score,
- 'max_score': latest_score.max_score,
- 'breakdown': latest_score.details,
- 'interpretation': interpretation
- },
- 'issues': latest_score.issues,
- 'suggestions': latest_score.suggestions,
- 'ai_suggestions': latest_score.ai_suggestions,
- 'scored_at': latest_score.created_at.isoformat()
- })
-
- except Exception as e:
- logger.error(f"Error fetching product score: {e}", exc_info=True)
- return JsonResponse({'error': str(e)}, status=500)
|