123 lines
5.1 KiB
Python
123 lines
5.1 KiB
Python
from rest_framework.views import APIView
|
|
from rest_framework.response import Response
|
|
from rest_framework import status
|
|
from django.db import connection
|
|
from .typesense_client import TypesenseService
|
|
import logging
|
|
import time
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
class ProductSearchView(APIView):
|
|
def get(self, request):
|
|
try:
|
|
q = request.query_params.get('q', '*').strip()
|
|
category = request.query_params.get('category', '').strip()
|
|
min_price = request.query_params.get('min_price')
|
|
max_price = request.query_params.get('max_price')
|
|
is_gi_tagged = request.query_params.get('is_gi_tagged')
|
|
supplier_id = request.query_params.get('supplier_id')
|
|
sort_by_param = request.query_params.get('sort_by', 'newest')
|
|
page = int(request.query_params.get('page', 1))
|
|
per_page = int(request.query_params.get('per_page', 20))
|
|
|
|
filter_clauses = ['is_active:=true']
|
|
|
|
if category:
|
|
filter_clauses.append(f'category:="{category}"')
|
|
if supplier_id:
|
|
filter_clauses.append(f'supplier_id:={supplier_id}')
|
|
if is_gi_tagged is not None and is_gi_tagged.lower() in ('true', '1'):
|
|
filter_clauses.append('is_gi_tagged:=true')
|
|
|
|
if min_price is not None or max_price is not None:
|
|
p_min = float(min_price) if min_price else 0.0
|
|
p_max = float(max_price) if max_price else 9999999.0
|
|
filter_clauses.append(f'price:[{p_min}..{p_max}]')
|
|
|
|
filter_by_str = ' && '.join(filter_clauses)
|
|
|
|
sort_by_str = 'created_at:desc'
|
|
if sort_by_param == 'price_asc':
|
|
sort_by_str = 'price:asc'
|
|
elif sort_by_param == 'price_desc':
|
|
sort_by_str = 'price:desc'
|
|
elif sort_by_param == 'relevance' and q != '*':
|
|
sort_by_str = ''
|
|
|
|
res = TypesenseService.search_products(
|
|
query=q,
|
|
filter_by=filter_by_str,
|
|
sort_by=sort_by_str,
|
|
page=page,
|
|
per_page=per_page
|
|
)
|
|
|
|
hits = res.get('hits', [])
|
|
found = res.get('found', 0)
|
|
total_pages = (found + per_page - 1) // per_page if per_page > 0 else 1
|
|
|
|
results = [hit.get('document', {}) for hit in hits]
|
|
facet_counts = res.get('facet_counts', [])
|
|
|
|
return Response({
|
|
'count': found,
|
|
'page': page,
|
|
'per_page': per_page,
|
|
'total_pages': total_pages,
|
|
'facets': facet_counts,
|
|
'results': results
|
|
}, status=status.HTTP_200_OK)
|
|
except Exception as e:
|
|
logger.error(f"Typesense Search Error: {e}", exc_info=True)
|
|
return Response({'error': str(e), 'results': [], 'count': 0}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
|
|
|
|
|
class IndexProductView(APIView):
|
|
def post(self, request):
|
|
try:
|
|
product_data = request.data
|
|
if not product_data or ('id' not in product_data and 'sku' not in product_data):
|
|
return Response({'error': 'Product id or sku is required'}, status=status.HTTP_400_BAD_REQUEST)
|
|
|
|
res = TypesenseService.index_product(product_data)
|
|
return Response({'status': 'indexed', 'result': res}, status=status.HTTP_200_OK)
|
|
except Exception as e:
|
|
logger.error(f"Product Index Error: {e}", exc_info=True)
|
|
return Response({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
|
|
|
|
|
class DeleteProductView(APIView):
|
|
def delete(self, request, product_id):
|
|
try:
|
|
res = TypesenseService.delete_product(product_id)
|
|
return Response({'status': 'deleted', 'result': res}, status=status.HTTP_200_OK)
|
|
except Exception as e:
|
|
logger.error(f"Product Delete Error: {e}", exc_info=True)
|
|
return Response({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
|
|
|
|
|
class ReindexCatalogView(APIView):
|
|
def post(self, request):
|
|
try:
|
|
indexed_count = 0
|
|
TypesenseService.ensure_schema()
|
|
|
|
with connection.cursor() as cursor:
|
|
cursor.execute("""
|
|
SELECT id, sku, title, description, category, supplier_id, price,
|
|
compare_at_price, stock, image, is_gi_tagged, is_active, status
|
|
FROM api_product
|
|
""")
|
|
columns = [col[0] for col in cursor.description]
|
|
rows = cursor.fetchall()
|
|
for row in rows:
|
|
item = dict(zip(columns, row))
|
|
item['created_at'] = int(time.time())
|
|
TypesenseService.index_product(item)
|
|
indexed_count += 1
|
|
|
|
return Response({'status': 'completed', 'total_indexed': indexed_count}, status=status.HTTP_200_OK)
|
|
except Exception as e:
|
|
logger.error(f"Full Reindex Error: {e}", exc_info=True)
|
|
return Response({'error': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|