Enhance API response handling and implement unified API standard across multiple modules. Added feature flags for unified exception handling and debug throttling in settings. Updated pagination and response formats in various viewsets to align with the new standard. Improved error handling and response validation in frontend components for better user feedback.
This commit is contained in:
@@ -6,6 +6,8 @@ from django.db import transaction, models
|
||||
from django.db.models import Q
|
||||
from igny8_core.api.base import SiteSectorModelViewSet
|
||||
from igny8_core.api.pagination import CustomPageNumberPagination
|
||||
from igny8_core.api.response import success_response, error_response
|
||||
from igny8_core.api.throttles import DebugScopedRateThrottle
|
||||
from .models import Tasks, Images, Content
|
||||
from .serializers import TasksSerializer, ImagesSerializer, ContentSerializer
|
||||
|
||||
@@ -13,10 +15,13 @@ from .serializers import TasksSerializer, ImagesSerializer, ContentSerializer
|
||||
class TasksViewSet(SiteSectorModelViewSet):
|
||||
"""
|
||||
ViewSet for managing tasks with CRUD operations
|
||||
Unified API Standard v1.0 compliant
|
||||
"""
|
||||
queryset = Tasks.objects.select_related('content_record')
|
||||
serializer_class = TasksSerializer
|
||||
pagination_class = CustomPageNumberPagination # Explicitly use custom pagination
|
||||
throttle_scope = 'writer'
|
||||
throttle_classes = [DebugScopedRateThrottle]
|
||||
|
||||
# DRF filtering configuration
|
||||
filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
|
||||
@@ -84,12 +89,16 @@ class TasksViewSet(SiteSectorModelViewSet):
|
||||
"""Bulk delete tasks"""
|
||||
ids = request.data.get('ids', [])
|
||||
if not ids:
|
||||
return Response({'error': 'No IDs provided'}, status=status.HTTP_400_BAD_REQUEST)
|
||||
return error_response(
|
||||
error='No IDs provided',
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
request=request
|
||||
)
|
||||
|
||||
queryset = self.get_queryset()
|
||||
deleted_count, _ = queryset.filter(id__in=ids).delete()
|
||||
|
||||
return Response({'deleted_count': deleted_count}, status=status.HTTP_200_OK)
|
||||
return success_response(data={'deleted_count': deleted_count}, request=request)
|
||||
|
||||
@action(detail=False, methods=['post'], url_path='bulk_update', url_name='bulk_update')
|
||||
def bulk_update(self, request):
|
||||
@@ -98,14 +107,22 @@ class TasksViewSet(SiteSectorModelViewSet):
|
||||
status_value = request.data.get('status')
|
||||
|
||||
if not ids:
|
||||
return Response({'error': 'No IDs provided'}, status=status.HTTP_400_BAD_REQUEST)
|
||||
return error_response(
|
||||
error='No IDs provided',
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
request=request
|
||||
)
|
||||
if not status_value:
|
||||
return Response({'error': 'No status provided'}, status=status.HTTP_400_BAD_REQUEST)
|
||||
return error_response(
|
||||
error='No status provided',
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
request=request
|
||||
)
|
||||
|
||||
queryset = self.get_queryset()
|
||||
updated_count = queryset.filter(id__in=ids).update(status=status_value)
|
||||
|
||||
return Response({'updated_count': updated_count}, status=status.HTTP_200_OK)
|
||||
return success_response(data={'updated_count': updated_count}, request=request)
|
||||
|
||||
@action(detail=False, methods=['post'], url_path='auto_generate_content', url_name='auto_generate_content')
|
||||
def auto_generate_content(self, request):
|
||||
@@ -120,17 +137,19 @@ class TasksViewSet(SiteSectorModelViewSet):
|
||||
ids = request.data.get('ids', [])
|
||||
if not ids:
|
||||
logger.warning("auto_generate_content: No IDs provided")
|
||||
return Response({
|
||||
'error': 'No IDs provided',
|
||||
'type': 'ValidationError'
|
||||
}, status=status.HTTP_400_BAD_REQUEST)
|
||||
return error_response(
|
||||
error='No IDs provided',
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
request=request
|
||||
)
|
||||
|
||||
if len(ids) > 10:
|
||||
logger.warning(f"auto_generate_content: Too many IDs provided: {len(ids)}")
|
||||
return Response({
|
||||
'error': 'Maximum 10 tasks allowed for content generation',
|
||||
'type': 'ValidationError'
|
||||
}, status=status.HTTP_400_BAD_REQUEST)
|
||||
return error_response(
|
||||
error='Maximum 10 tasks allowed for content generation',
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
request=request
|
||||
)
|
||||
|
||||
logger.info(f"auto_generate_content: Processing {len(ids)} task IDs: {ids}")
|
||||
|
||||
@@ -151,11 +170,11 @@ class TasksViewSet(SiteSectorModelViewSet):
|
||||
|
||||
if existing_count == 0:
|
||||
logger.error(f"auto_generate_content: No tasks found for IDs: {ids}")
|
||||
return Response({
|
||||
'error': f'No tasks found for the provided IDs: {ids}',
|
||||
'type': 'NotFound',
|
||||
'requested_ids': ids
|
||||
}, status=status.HTTP_404_NOT_FOUND)
|
||||
return error_response(
|
||||
error=f'No tasks found for the provided IDs: {ids}',
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
request=request
|
||||
)
|
||||
|
||||
if existing_count < len(ids):
|
||||
missing_ids = set(ids) - set(existing_ids)
|
||||
@@ -171,11 +190,11 @@ class TasksViewSet(SiteSectorModelViewSet):
|
||||
logger.error(f" - Account ID: {account_id}")
|
||||
logger.error("=" * 80, exc_info=True)
|
||||
|
||||
return Response({
|
||||
'error': f'Database error while querying tasks: {str(db_error)}',
|
||||
'type': 'OperationalError',
|
||||
'details': 'Failed to retrieve tasks from database. Please check database connection and try again.'
|
||||
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
return error_response(
|
||||
error=f'Database error while querying tasks: {str(db_error)}',
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
request=request
|
||||
)
|
||||
|
||||
# Try to queue Celery task, fall back to synchronous if Celery not available
|
||||
try:
|
||||
@@ -192,11 +211,11 @@ class TasksViewSet(SiteSectorModelViewSet):
|
||||
account_id=account_id
|
||||
)
|
||||
logger.info(f"auto_generate_content: Celery task queued successfully: {task.id}")
|
||||
return Response({
|
||||
'success': True,
|
||||
'task_id': str(task.id),
|
||||
'message': 'Content generation started'
|
||||
}, status=status.HTTP_200_OK)
|
||||
return success_response(
|
||||
data={'task_id': str(task.id)},
|
||||
message='Content generation started',
|
||||
request=request
|
||||
)
|
||||
except KombuOperationalError as celery_error:
|
||||
logger.error("=" * 80)
|
||||
logger.error("CELERY ERROR: Failed to queue task")
|
||||
@@ -206,10 +225,11 @@ class TasksViewSet(SiteSectorModelViewSet):
|
||||
logger.error(f" - Account ID: {account_id}")
|
||||
logger.error("=" * 80, exc_info=True)
|
||||
|
||||
return Response({
|
||||
'error': 'Task queue unavailable. Please try again.',
|
||||
'type': 'QueueError'
|
||||
}, status=status.HTTP_503_SERVICE_UNAVAILABLE)
|
||||
return error_response(
|
||||
error='Task queue unavailable. Please try again.',
|
||||
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
|
||||
request=request
|
||||
)
|
||||
except Exception as celery_error:
|
||||
logger.error("=" * 80)
|
||||
logger.error("CELERY ERROR: Failed to queue task")
|
||||
@@ -227,16 +247,17 @@ class TasksViewSet(SiteSectorModelViewSet):
|
||||
account_id=account_id
|
||||
)
|
||||
if result.get('success'):
|
||||
return Response({
|
||||
'success': True,
|
||||
'tasks_updated': result.get('count', 0),
|
||||
'message': 'Content generated successfully (synchronous)'
|
||||
}, status=status.HTTP_200_OK)
|
||||
return success_response(
|
||||
data={'tasks_updated': result.get('count', 0)},
|
||||
message='Content generated successfully (synchronous)',
|
||||
request=request
|
||||
)
|
||||
else:
|
||||
return Response({
|
||||
'error': result.get('error', 'Content generation failed'),
|
||||
'type': 'TaskExecutionError'
|
||||
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
return error_response(
|
||||
error=result.get('error', 'Content generation failed'),
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
request=request
|
||||
)
|
||||
else:
|
||||
# Celery not available - execute synchronously
|
||||
logger.info(f"auto_generate_content: Executing synchronously (Celery not available)")
|
||||
@@ -247,17 +268,18 @@ class TasksViewSet(SiteSectorModelViewSet):
|
||||
)
|
||||
if result.get('success'):
|
||||
logger.info(f"auto_generate_content: Synchronous execution successful: {result.get('count', 0)} tasks updated")
|
||||
return Response({
|
||||
'success': True,
|
||||
'tasks_updated': result.get('count', 0),
|
||||
'message': 'Content generated successfully'
|
||||
}, status=status.HTTP_200_OK)
|
||||
return success_response(
|
||||
data={'tasks_updated': result.get('count', 0)},
|
||||
message='Content generated successfully',
|
||||
request=request
|
||||
)
|
||||
else:
|
||||
logger.error(f"auto_generate_content: Synchronous execution failed: {result.get('error', 'Unknown error')}")
|
||||
return Response({
|
||||
'error': result.get('error', 'Content generation failed'),
|
||||
'type': 'TaskExecutionError'
|
||||
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
return error_response(
|
||||
error=result.get('error', 'Content generation failed'),
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
request=request
|
||||
)
|
||||
|
||||
except ImportError as import_error:
|
||||
logger.error(f"auto_generate_content: ImportError - tasks module not available: {str(import_error)}")
|
||||
@@ -268,21 +290,22 @@ class TasksViewSet(SiteSectorModelViewSet):
|
||||
updated_count = tasks.update(status='completed', content='[AI content generation not available]')
|
||||
|
||||
logger.info(f"auto_generate_content: Updated {updated_count} tasks (AI generation not available)")
|
||||
return Response({
|
||||
'updated_count': updated_count,
|
||||
'message': 'Tasks updated (AI generation not available)'
|
||||
}, status=status.HTTP_200_OK)
|
||||
return success_response(
|
||||
data={'updated_count': updated_count},
|
||||
message='Tasks updated (AI generation not available)',
|
||||
request=request
|
||||
)
|
||||
except (OperationalError, DatabaseError) as db_error:
|
||||
logger.error("=" * 80)
|
||||
logger.error("DATABASE ERROR: Failed to update tasks")
|
||||
logger.error(f" - Error type: {type(db_error).__name__}")
|
||||
logger.error(f" - Error message: {str(db_error)}")
|
||||
logger.error("=" * 80, exc_info=True)
|
||||
return Response({
|
||||
'error': f'Database error while updating tasks: {str(db_error)}',
|
||||
'type': 'OperationalError',
|
||||
'details': 'Failed to update tasks in database. Please check database connection.'
|
||||
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
return error_response(
|
||||
error=f'Database error while updating tasks: {str(db_error)}',
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
request=request
|
||||
)
|
||||
|
||||
except (OperationalError, DatabaseError) as db_error:
|
||||
logger.error("=" * 80)
|
||||
@@ -293,11 +316,11 @@ class TasksViewSet(SiteSectorModelViewSet):
|
||||
logger.error(f" - Account ID: {account_id}")
|
||||
logger.error("=" * 80, exc_info=True)
|
||||
|
||||
return Response({
|
||||
'error': f'Database error during content generation: {str(db_error)}',
|
||||
'type': 'OperationalError',
|
||||
'details': 'A database operation failed. This may be due to connection issues, constraint violations, or data integrity problems. Check the logs for more details.'
|
||||
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
return error_response(
|
||||
error=f'Database error during content generation: {str(db_error)}',
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
request=request
|
||||
)
|
||||
|
||||
except IntegrityError as integrity_error:
|
||||
logger.error("=" * 80)
|
||||
@@ -306,18 +329,19 @@ class TasksViewSet(SiteSectorModelViewSet):
|
||||
logger.error(f" - Task IDs: {ids}")
|
||||
logger.error("=" * 80, exc_info=True)
|
||||
|
||||
return Response({
|
||||
'error': f'Data integrity error: {str(integrity_error)}',
|
||||
'type': 'IntegrityError',
|
||||
'details': 'The operation violated database constraints. This may indicate missing required relationships or invalid data.'
|
||||
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
return error_response(
|
||||
error=f'Data integrity error: {str(integrity_error)}',
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
request=request
|
||||
)
|
||||
|
||||
except ValidationError as validation_error:
|
||||
logger.error(f"auto_generate_content: ValidationError: {str(validation_error)}")
|
||||
return Response({
|
||||
'error': f'Validation error: {str(validation_error)}',
|
||||
'type': 'ValidationError'
|
||||
}, status=status.HTTP_400_BAD_REQUEST)
|
||||
return error_response(
|
||||
error=f'Validation error: {str(validation_error)}',
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
request=request
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.error("=" * 80)
|
||||
@@ -328,11 +352,11 @@ class TasksViewSet(SiteSectorModelViewSet):
|
||||
logger.error(f" - Account ID: {account_id}")
|
||||
logger.error("=" * 80, exc_info=True)
|
||||
|
||||
return Response({
|
||||
'error': f'Unexpected error: {str(e)}',
|
||||
'type': type(e).__name__,
|
||||
'details': 'An unexpected error occurred. Please check the logs for more details.'
|
||||
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
return error_response(
|
||||
error=f'Unexpected error: {str(e)}',
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
request=request
|
||||
)
|
||||
|
||||
except Exception as outer_error:
|
||||
logger.error("=" * 80)
|
||||
@@ -341,10 +365,11 @@ class TasksViewSet(SiteSectorModelViewSet):
|
||||
logger.error(f" - Error message: {str(outer_error)}")
|
||||
logger.error("=" * 80, exc_info=True)
|
||||
|
||||
return Response({
|
||||
'error': f'Critical error: {str(outer_error)}',
|
||||
'type': type(outer_error).__name__
|
||||
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
return error_response(
|
||||
error=f'Critical error: {str(outer_error)}',
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
request=request
|
||||
)
|
||||
|
||||
|
||||
class ImagesViewSet(SiteSectorModelViewSet):
|
||||
@@ -383,30 +408,38 @@ class ImagesViewSet(SiteSectorModelViewSet):
|
||||
try:
|
||||
image = Images.objects.get(pk=pk)
|
||||
except Images.DoesNotExist:
|
||||
return Response({
|
||||
'error': 'Image not found'
|
||||
}, status=status.HTTP_404_NOT_FOUND)
|
||||
return error_response(
|
||||
error='Image not found',
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
request=request
|
||||
)
|
||||
|
||||
# Check if image has a local path
|
||||
if not image.image_path:
|
||||
return Response({
|
||||
'error': 'No local file path available for this image'
|
||||
}, status=status.HTTP_404_NOT_FOUND)
|
||||
return error_response(
|
||||
error='No local file path available for this image',
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
request=request
|
||||
)
|
||||
|
||||
file_path = image.image_path
|
||||
|
||||
# Verify file exists at the saved path
|
||||
if not os.path.exists(file_path):
|
||||
logger.error(f"[serve_image_file] Image {pk} - File not found at saved path: {file_path}")
|
||||
return Response({
|
||||
'error': f'Image file not found at: {file_path}'
|
||||
}, status=status.HTTP_404_NOT_FOUND)
|
||||
return error_response(
|
||||
error=f'Image file not found at: {file_path}',
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
request=request
|
||||
)
|
||||
|
||||
# Check if file is readable
|
||||
if not os.access(file_path, os.R_OK):
|
||||
return Response({
|
||||
'error': 'Image file is not readable'
|
||||
}, status=status.HTTP_403_FORBIDDEN)
|
||||
return error_response(
|
||||
error='Image file is not readable',
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
request=request
|
||||
)
|
||||
|
||||
# Determine content type from file extension
|
||||
import mimetypes
|
||||
@@ -422,31 +455,45 @@ class ImagesViewSet(SiteSectorModelViewSet):
|
||||
filename=os.path.basename(file_path)
|
||||
)
|
||||
except Exception as e:
|
||||
return Response({
|
||||
'error': f'Failed to serve file: {str(e)}'
|
||||
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
return error_response(
|
||||
error=f'Failed to serve file: {str(e)}',
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
request=request
|
||||
)
|
||||
|
||||
except Images.DoesNotExist:
|
||||
return Response({
|
||||
'error': 'Image not found'
|
||||
}, status=status.HTTP_404_NOT_FOUND)
|
||||
return error_response(
|
||||
error='Image not found',
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
request=request
|
||||
)
|
||||
except Exception as e:
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
logger.error(f"Error serving image file: {str(e)}", exc_info=True)
|
||||
return Response({
|
||||
'error': f'Failed to serve image: {str(e)}'
|
||||
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
return error_response(
|
||||
error=f'Failed to serve image: {str(e)}',
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
request=request
|
||||
)
|
||||
|
||||
@action(detail=False, methods=['post'], url_path='auto_generate', url_name='auto_generate_images')
|
||||
def auto_generate_images(self, request):
|
||||
"""Auto-generate images for tasks using AI"""
|
||||
task_ids = request.data.get('task_ids', [])
|
||||
if not task_ids:
|
||||
return Response({'error': 'No task IDs provided'}, status=status.HTTP_400_BAD_REQUEST)
|
||||
return error_response(
|
||||
error='No task IDs provided',
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
request=request
|
||||
)
|
||||
|
||||
if len(task_ids) > 10:
|
||||
return Response({'error': 'Maximum 10 tasks allowed for image generation'}, status=status.HTTP_400_BAD_REQUEST)
|
||||
return error_response(
|
||||
error='Maximum 10 tasks allowed for image generation',
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
request=request
|
||||
)
|
||||
|
||||
# Get account
|
||||
account = getattr(request, 'account', None)
|
||||
@@ -464,11 +511,11 @@ class ImagesViewSet(SiteSectorModelViewSet):
|
||||
payload={'ids': task_ids},
|
||||
account_id=account_id
|
||||
)
|
||||
return Response({
|
||||
'success': True,
|
||||
'task_id': str(task.id),
|
||||
'message': 'Image generation started'
|
||||
}, status=status.HTTP_200_OK)
|
||||
return success_response(
|
||||
data={'task_id': str(task.id)},
|
||||
message='Image generation started',
|
||||
request=request
|
||||
)
|
||||
else:
|
||||
# Celery not available - execute synchronously
|
||||
result = run_ai_task(
|
||||
@@ -477,33 +524,39 @@ class ImagesViewSet(SiteSectorModelViewSet):
|
||||
account_id=account_id
|
||||
)
|
||||
if result.get('success'):
|
||||
return Response({
|
||||
'success': True,
|
||||
'images_created': result.get('count', 0),
|
||||
'message': result.get('message', 'Image generation completed')
|
||||
}, status=status.HTTP_200_OK)
|
||||
return success_response(
|
||||
data={'images_created': result.get('count', 0)},
|
||||
message=result.get('message', 'Image generation completed'),
|
||||
request=request
|
||||
)
|
||||
else:
|
||||
return Response({
|
||||
'error': result.get('error', 'Image generation failed')
|
||||
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
return error_response(
|
||||
error=result.get('error', 'Image generation failed'),
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
request=request
|
||||
)
|
||||
except KombuOperationalError as e:
|
||||
return Response({
|
||||
'error': 'Task queue unavailable. Please try again.',
|
||||
'type': 'QueueError'
|
||||
}, status=status.HTTP_503_SERVICE_UNAVAILABLE)
|
||||
return error_response(
|
||||
error='Task queue unavailable. Please try again.',
|
||||
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
|
||||
request=request
|
||||
)
|
||||
except ImportError:
|
||||
# Tasks module not available
|
||||
return Response({
|
||||
'error': 'Image generation task not available'
|
||||
}, status=status.HTTP_503_SERVICE_UNAVAILABLE)
|
||||
return error_response(
|
||||
error='Image generation task not available',
|
||||
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
|
||||
request=request
|
||||
)
|
||||
except Exception as e:
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
logger.error(f"Error queuing image generation task: {str(e)}", exc_info=True)
|
||||
return Response({
|
||||
'error': f'Failed to start image generation: {str(e)}',
|
||||
'type': 'TaskError'
|
||||
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
return error_response(
|
||||
error=f'Failed to start image generation: {str(e)}',
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
request=request
|
||||
)
|
||||
|
||||
@action(detail=False, methods=['post'], url_path='bulk_update', url_name='bulk_update')
|
||||
def bulk_update(self, request):
|
||||
@@ -518,7 +571,11 @@ class ImagesViewSet(SiteSectorModelViewSet):
|
||||
status_value = request.data.get('status')
|
||||
|
||||
if not status_value:
|
||||
return Response({'error': 'No status provided'}, status=status.HTTP_400_BAD_REQUEST)
|
||||
return error_response(
|
||||
error='No status provided',
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
request=request
|
||||
)
|
||||
|
||||
queryset = self.get_queryset()
|
||||
|
||||
@@ -534,13 +591,21 @@ class ImagesViewSet(SiteSectorModelViewSet):
|
||||
Q(content=content) | Q(task=content.task)
|
||||
).update(status=status_value)
|
||||
except Content.DoesNotExist:
|
||||
return Response({'error': 'Content not found'}, status=status.HTTP_404_NOT_FOUND)
|
||||
return error_response(
|
||||
error='Content not found',
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
request=request
|
||||
)
|
||||
elif image_ids:
|
||||
updated_count = queryset.filter(id__in=image_ids).update(status=status_value)
|
||||
else:
|
||||
return Response({'error': 'Either content_id or ids must be provided'}, status=status.HTTP_400_BAD_REQUEST)
|
||||
return error_response(
|
||||
error='Either content_id or ids must be provided',
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
request=request
|
||||
)
|
||||
|
||||
return Response({'updated_count': updated_count}, status=status.HTTP_200_OK)
|
||||
return success_response(data={'updated_count': updated_count}, request=request)
|
||||
|
||||
@action(detail=False, methods=['get'], url_path='content_images', url_name='content_images')
|
||||
def content_images(self, request):
|
||||
@@ -621,10 +686,13 @@ class ImagesViewSet(SiteSectorModelViewSet):
|
||||
# Sort by content title
|
||||
grouped_data.sort(key=lambda x: x['content_title'])
|
||||
|
||||
return Response({
|
||||
'count': len(grouped_data),
|
||||
'results': grouped_data
|
||||
}, status=status.HTTP_200_OK)
|
||||
return success_response(
|
||||
data={
|
||||
'count': len(grouped_data),
|
||||
'results': grouped_data
|
||||
},
|
||||
request=request
|
||||
)
|
||||
|
||||
@action(detail=False, methods=['post'], url_path='generate_images', url_name='generate_images')
|
||||
def generate_images(self, request):
|
||||
@@ -636,10 +704,11 @@ class ImagesViewSet(SiteSectorModelViewSet):
|
||||
content_id = request.data.get('content_id')
|
||||
|
||||
if not image_ids:
|
||||
return Response({
|
||||
'error': 'No image IDs provided',
|
||||
'type': 'ValidationError'
|
||||
}, status=status.HTTP_400_BAD_REQUEST)
|
||||
return error_response(
|
||||
error='No image IDs provided',
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
request=request
|
||||
)
|
||||
|
||||
account_id = account.id if account else None
|
||||
|
||||
@@ -651,11 +720,11 @@ class ImagesViewSet(SiteSectorModelViewSet):
|
||||
account_id=account_id,
|
||||
content_id=content_id
|
||||
)
|
||||
return Response({
|
||||
'success': True,
|
||||
'task_id': str(task.id),
|
||||
'message': 'Image generation started'
|
||||
}, status=status.HTTP_200_OK)
|
||||
return success_response(
|
||||
data={'task_id': str(task.id)},
|
||||
message='Image generation started',
|
||||
request=request
|
||||
)
|
||||
else:
|
||||
# Fallback to synchronous execution (for testing)
|
||||
result = process_image_generation_queue(
|
||||
@@ -663,21 +732,25 @@ class ImagesViewSet(SiteSectorModelViewSet):
|
||||
account_id=account_id,
|
||||
content_id=content_id
|
||||
)
|
||||
return Response(result, status=status.HTTP_200_OK)
|
||||
return success_response(data=result, request=request)
|
||||
except Exception as e:
|
||||
logger.error(f"[generate_images] Error: {str(e)}", exc_info=True)
|
||||
return Response({
|
||||
'error': str(e),
|
||||
'type': 'ExecutionError'
|
||||
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
return error_response(
|
||||
error=str(e),
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
request=request
|
||||
)
|
||||
|
||||
class ContentViewSet(SiteSectorModelViewSet):
|
||||
"""
|
||||
ViewSet for managing task content
|
||||
Unified API Standard v1.0 compliant
|
||||
"""
|
||||
queryset = Content.objects.all()
|
||||
serializer_class = ContentSerializer
|
||||
pagination_class = CustomPageNumberPagination
|
||||
throttle_scope = 'writer'
|
||||
throttle_classes = [DebugScopedRateThrottle]
|
||||
|
||||
filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
|
||||
search_fields = ['title', 'meta_title', 'primary_keyword']
|
||||
@@ -702,10 +775,11 @@ class ContentViewSet(SiteSectorModelViewSet):
|
||||
ids = request.data.get('ids', [])
|
||||
|
||||
if not ids:
|
||||
return Response({
|
||||
'error': 'No IDs provided',
|
||||
'type': 'ValidationError'
|
||||
}, status=status.HTTP_400_BAD_REQUEST)
|
||||
return error_response(
|
||||
error='No IDs provided',
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
request=request
|
||||
)
|
||||
|
||||
account_id = account.id if account else None
|
||||
|
||||
@@ -717,11 +791,11 @@ class ContentViewSet(SiteSectorModelViewSet):
|
||||
payload={'ids': ids},
|
||||
account_id=account_id
|
||||
)
|
||||
return Response({
|
||||
'success': True,
|
||||
'task_id': str(task.id),
|
||||
'message': 'Image prompt generation started'
|
||||
}, status=status.HTTP_200_OK)
|
||||
return success_response(
|
||||
data={'task_id': str(task.id)},
|
||||
message='Image prompt generation started',
|
||||
request=request
|
||||
)
|
||||
else:
|
||||
# Fallback to synchronous execution
|
||||
result = run_ai_task(
|
||||
@@ -730,19 +804,21 @@ class ContentViewSet(SiteSectorModelViewSet):
|
||||
account_id=account_id
|
||||
)
|
||||
if result.get('success'):
|
||||
return Response({
|
||||
'success': True,
|
||||
'prompts_created': result.get('count', 0),
|
||||
'message': 'Image prompts generated successfully'
|
||||
}, status=status.HTTP_200_OK)
|
||||
return success_response(
|
||||
data={'prompts_created': result.get('count', 0)},
|
||||
message='Image prompts generated successfully',
|
||||
request=request
|
||||
)
|
||||
else:
|
||||
return Response({
|
||||
'error': result.get('error', 'Image prompt generation failed'),
|
||||
'type': 'TaskExecutionError'
|
||||
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
return error_response(
|
||||
error=result.get('error', 'Image prompt generation failed'),
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
request=request
|
||||
)
|
||||
except Exception as e:
|
||||
return Response({
|
||||
'error': str(e),
|
||||
'type': 'ExecutionError'
|
||||
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
||||
return error_response(
|
||||
error=str(e),
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
request=request
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user