views.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  1. # views.py (Enhanced)
  2. from django.shortcuts import render, get_object_or_404
  3. from django.http import JsonResponse
  4. from django.views import View
  5. from django.core.cache import cache
  6. import json
  7. import logging
  8. from core.models import AttributeScore, CategoryAttributeRule, Product
  9. from core.services.attribute_scorer import AttributeQualityScorer
  10. from django.views.decorators.csrf import csrf_exempt
  11. from django.utils.decorators import method_decorator
  12. logger = logging.getLogger(__name__)
  13. # @method_decorator(csrf_exempt, name='dispatch')
  14. # class AttributeScoreView(View):
  15. # """Enhanced API view with caching and better error handling"""
  16. # def __init__(self, *args, **kwargs):
  17. # super().__init__(*args, **kwargs)
  18. # self.scorer = AttributeQualityScorer(use_ai=True)
  19. # def post(self, request, *args, **kwargs):
  20. # """Score a single product with AI suggestions"""
  21. # try:
  22. # data = json.loads(request.body)
  23. # product_data = data.get('product', {})
  24. # sku = product_data.get('sku')
  25. # use_ai = data.get('use_ai', True)
  26. # if not sku:
  27. # return JsonResponse({'error': 'SKU is required'}, status=400)
  28. # # Validate category
  29. # category = product_data.get('category', '')
  30. # if not category:
  31. # return JsonResponse({'error': 'Category is required'}, status=400)
  32. # # Get or create product
  33. # product, created = Product.objects.get_or_create(
  34. # sku=sku,
  35. # defaults={
  36. # 'title': product_data.get('title', ''),
  37. # 'description': product_data.get('description', ''),
  38. # 'category': category,
  39. # 'attributes': product_data.get('attributes', {})
  40. # }
  41. # )
  42. # # Update if exists
  43. # if not created:
  44. # product.title = product_data.get('title', product.title)
  45. # product.description = product_data.get('description', product.description)
  46. # product.attributes = product_data.get('attributes', product.attributes)
  47. # product.save()
  48. # # Get category rules (with caching)
  49. # cache_key = f"category_rules_{category}"
  50. # rules = cache.get(cache_key)
  51. # if rules is None:
  52. # rules = list(CategoryAttributeRule.objects.filter(category=category).values())
  53. # cache.set(cache_key, rules, 3600) # Cache for 1 hour
  54. # if not rules:
  55. # return JsonResponse({
  56. # 'error': f'No rules defined for category: {category}',
  57. # 'suggestion': 'Please configure category rules first'
  58. # }, status=400)
  59. # # Score the product
  60. # score_result = self.scorer.score_product(
  61. # {
  62. # 'sku': product.sku,
  63. # 'category': product.category,
  64. # 'title': product.title,
  65. # 'description': product.description,
  66. # 'attributes': product.attributes
  67. # },
  68. # rules,
  69. # generate_ai_suggestions=use_ai
  70. # )
  71. # # Save score
  72. # AttributeScore.objects.create(
  73. # product=product,
  74. # score=score_result['final_score'],
  75. # max_score=score_result['max_score'],
  76. # details=score_result['breakdown'],
  77. # issues=score_result['issues'],
  78. # suggestions=score_result['suggestions'],
  79. # ai_suggestions=score_result.get('ai_suggestions', {}),
  80. # processing_time=score_result.get('processing_time', 0)
  81. # )
  82. # return JsonResponse({
  83. # 'success': True,
  84. # 'product_sku': sku,
  85. # 'created': created,
  86. # 'score_result': score_result
  87. # })
  88. # except json.JSONDecodeError:
  89. # return JsonResponse({'error': 'Invalid JSON'}, status=400)
  90. # except Exception as e:
  91. # logger.error(f"Error scoring product: {str(e)}", exc_info=True)
  92. # return JsonResponse({'error': str(e)}, status=500)
  93. # def get(self, request, sku=None):
  94. # """Get latest score for a product"""
  95. # if not sku:
  96. # return JsonResponse({'error': 'SKU parameter required'}, status=400)
  97. # try:
  98. # product = get_object_or_404(Product, sku=sku)
  99. # latest_score = product.attribute_scores.order_by('-created_at').first()
  100. # if not latest_score:
  101. # return JsonResponse({
  102. # 'message': 'No scores found for this product',
  103. # 'sku': sku
  104. # }, status=404)
  105. # return JsonResponse({
  106. # 'sku': product.sku,
  107. # 'title': product.title,
  108. # 'category': product.category,
  109. # 'attributes': product.attributes,
  110. # 'score': latest_score.score,
  111. # 'max_score': latest_score.max_score,
  112. # 'details': latest_score.details,
  113. # 'issues': latest_score.issues,
  114. # 'suggestions': latest_score.suggestions,
  115. # 'ai_suggestions': latest_score.ai_suggestions,
  116. # 'processing_time': latest_score.processing_time,
  117. # 'scored_at': latest_score.created_at.isoformat()
  118. # })
  119. # except Exception as e:
  120. # logger.error(f"Error retrieving score: {str(e)}")
  121. # return JsonResponse({'error': str(e)}, status=500)
  122. @method_decorator(csrf_exempt, name='dispatch')
  123. class AttributeScoreView(View):
  124. """Enhanced API view with caching and AI suggestions"""
  125. def __init__(self, *args, **kwargs):
  126. super().__init__(*args, **kwargs)
  127. self.scorer = AttributeQualityScorer(use_ai=True) # enable AI
  128. def post(self, request, *args, **kwargs):
  129. """Score a single product with AI suggestions"""
  130. try:
  131. data = json.loads(request.body)
  132. product_data = data.get('product', {})
  133. sku = product_data.get('sku')
  134. use_ai = data.get('use_ai', True)
  135. if not sku:
  136. return JsonResponse({'error': 'SKU is required'}, status=400)
  137. category = product_data.get('category', '')
  138. if not category:
  139. return JsonResponse({'error': 'Category is required'}, status=400)
  140. # Get or create product
  141. product, created = Product.objects.get_or_create(
  142. sku=sku,
  143. defaults={
  144. 'title': product_data.get('title', ''),
  145. 'description': product_data.get('description', ''),
  146. 'category': category,
  147. 'attributes': product_data.get('attributes', {})
  148. }
  149. )
  150. # Update if exists
  151. if not created:
  152. product.title = product_data.get('title', product.title)
  153. product.description = product_data.get('description', product.description)
  154. product.attributes = product_data.get('attributes', product.attributes)
  155. product.save()
  156. # Get rules (cached)
  157. cache_key = f"category_rules_{category}"
  158. rules = cache.get(cache_key)
  159. if rules is None:
  160. rules = list(CategoryAttributeRule.objects.filter(category=category).values())
  161. cache.set(cache_key, rules, 3600)
  162. if not rules:
  163. return JsonResponse({'error': f'No rules defined for {category}'}, status=400)
  164. # Force AI suggestions
  165. score_result = self.scorer.score_product(
  166. {
  167. 'sku': product.sku,
  168. 'category': product.category,
  169. 'title': product.title,
  170. 'description': product.description,
  171. 'attributes': product.attributes
  172. },
  173. rules,
  174. generate_ai_suggestions=True # always generate AI
  175. )
  176. # Save score
  177. AttributeScore.objects.create(
  178. product=product,
  179. score=score_result['final_score'],
  180. max_score=score_result['max_score'],
  181. details=score_result['breakdown'],
  182. issues=score_result['issues'],
  183. suggestions=score_result['suggestions'],
  184. ai_suggestions=score_result.get('ai_suggestions', {}),
  185. processing_time=score_result.get('processing_time', 0)
  186. )
  187. return JsonResponse({
  188. 'success': True,
  189. 'product_sku': sku,
  190. 'created': created,
  191. 'score_result': score_result
  192. })
  193. except json.JSONDecodeError:
  194. return JsonResponse({'error': 'Invalid JSON'}, status=400)
  195. except Exception as e:
  196. logger.error(f"Error scoring product: {str(e)}", exc_info=True)
  197. return JsonResponse({'error': str(e)}, status=500)
  198. from django.views import View
  199. from django.http import JsonResponse
  200. from django.utils.decorators import method_decorator
  201. from django.views.decorators.csrf import csrf_exempt
  202. import json
  203. import logging
  204. from .models import Product, CategoryAttributeRule, AttributeScore
  205. from .services.attribute_scorer import AttributeQualityScorer
  206. logger = logging.getLogger(__name__)
  207. # @method_decorator(csrf_exempt, name='dispatch')
  208. # class BatchScoreView(View):
  209. # """Batch scoring endpoint with AI suggestions"""
  210. # def __init__(self, *args, **kwargs):
  211. # super().__init__(*args, **kwargs)
  212. # self.scorer = AttributeQualityScorer(use_ai=True) # AI enabled
  213. # def post(self, request):
  214. # """Score multiple products"""
  215. # try:
  216. # data = json.loads(request.body)
  217. # products = data.get('products', [])
  218. # if not products:
  219. # return JsonResponse({'error': 'No products provided'}, status=400)
  220. # results = []
  221. # errors = []
  222. # for product_data in products[:100]: # Limit to 100 products
  223. # sku = product_data.get('sku')
  224. # category = product_data.get('category')
  225. # if not sku or not category:
  226. # errors.append({'sku': sku, 'error': 'Missing SKU or category'})
  227. # continue
  228. # try:
  229. # # Get category rules
  230. # rules = list(CategoryAttributeRule.objects.filter(category=category).values())
  231. # if not rules:
  232. # errors.append({'sku': sku, 'error': f'No rules defined for category {category}'})
  233. # continue
  234. # # Score with AI suggestions enabled
  235. # score_result = self.scorer.score_product(
  236. # product_data,
  237. # rules,
  238. # generate_ai_suggestions=True
  239. # )
  240. # # Save score in DB
  241. # product, created = Product.objects.get_or_create(
  242. # sku=sku,
  243. # defaults={
  244. # 'title': product_data.get('title', ''),
  245. # 'description': product_data.get('description', ''),
  246. # 'category': category,
  247. # 'attributes': product_data.get('attributes', {})
  248. # }
  249. # )
  250. # if not created:
  251. # product.title = product_data.get('title', product.title)
  252. # product.description = product_data.get('description', product.description)
  253. # product.attributes = product_data.get('attributes', product.attributes)
  254. # product.save()
  255. # AttributeScore.objects.create(
  256. # product=product,
  257. # score=score_result['final_score'],
  258. # max_score=score_result['max_score'],
  259. # details=score_result['breakdown'],
  260. # issues=score_result['issues'],
  261. # suggestions=score_result['suggestions'],
  262. # ai_suggestions=score_result.get('ai_suggestions', {}),
  263. # processing_time=score_result.get('processing_time', 0)
  264. # )
  265. # results.append({
  266. # 'sku': sku,
  267. # 'final_score': score_result['final_score'],
  268. # 'max_score': score_result['max_score'],
  269. # 'breakdown': score_result['breakdown'],
  270. # 'issues': score_result['issues'],
  271. # 'suggestions': score_result['suggestions'],
  272. # 'ai_suggestions': score_result.get('ai_suggestions', {}),
  273. # 'processing_time': score_result.get('processing_time', 0)
  274. # })
  275. # except Exception as e:
  276. # logger.error(f"Error scoring SKU {sku}: {str(e)}", exc_info=True)
  277. # errors.append({'sku': sku, 'error': str(e)})
  278. # return JsonResponse({
  279. # 'success': True,
  280. # 'processed': len(results),
  281. # 'results': results,
  282. # 'errors': errors
  283. # })
  284. # except Exception as e:
  285. # logger.error(f"Batch scoring error: {str(e)}", exc_info=True)
  286. # return JsonResponse({'error': str(e)}, status=500)
  287. @method_decorator(csrf_exempt, name='dispatch')
  288. class BatchScoreView(View):
  289. """Batch scoring with AI suggestions"""
  290. def __init__(self, *args, **kwargs):
  291. super().__init__(*args, **kwargs)
  292. self.scorer = AttributeQualityScorer(use_ai=True) # enable AI even for batch
  293. def post(self, request):
  294. try:
  295. data = json.loads(request.body)
  296. products = data.get('products', [])
  297. if not products:
  298. return JsonResponse({'error': 'No products provided'}, status=400)
  299. results = []
  300. errors = []
  301. for product_data in products[:100]: # limit 100
  302. try:
  303. sku = product_data.get('sku')
  304. category = product_data.get('category')
  305. if not sku or not category:
  306. errors.append({'sku': sku, 'error': 'Missing SKU or category'})
  307. continue
  308. # Get rules
  309. rules = list(CategoryAttributeRule.objects.filter(category=category).values())
  310. if not rules:
  311. errors.append({'sku': sku, 'error': f'No rules for category {category}'})
  312. continue
  313. # Force AI suggestions
  314. score_result = self.scorer.score_product(
  315. product_data,
  316. rules,
  317. generate_ai_suggestions=True # <- key change
  318. )
  319. results.append({
  320. 'sku': sku,
  321. 'final_score': score_result['final_score'],
  322. 'max_score': score_result['max_score'],
  323. 'breakdown': score_result['breakdown'],
  324. 'issues': score_result['issues'],
  325. 'suggestions': score_result['suggestions'],
  326. 'ai_suggestions': score_result.get('ai_suggestions', {}),
  327. 'processing_time': score_result.get('processing_time', 0)
  328. })
  329. except Exception as e:
  330. errors.append({'sku': product_data.get('sku'), 'error': str(e)})
  331. return JsonResponse({
  332. 'success': True,
  333. 'processed': len(results),
  334. 'results': results,
  335. 'errors': errors
  336. })
  337. except Exception as e:
  338. logger.error(f"Batch scoring error: {str(e)}")
  339. return JsonResponse({'error': str(e)}, status=500)