stage1 part b

This commit is contained in:
alorig
2025-11-24 13:42:03 +05:00
parent ef735eb70b
commit f63ce92587
11 changed files with 1327 additions and 376 deletions

View File

@@ -16,15 +16,16 @@ from .serializers import (
ImagesSerializer,
ContentSerializer,
ContentTaxonomySerializer,
ContentAttributeSerializer,
# ContentAttributeSerializer removed in Stage 1 - model no longer exists
)
from igny8_core.business.content.models import ContentTaxonomy, ContentAttribute
from igny8_core.business.content.models import ContentTaxonomy # ContentAttribute removed in Stage 1
from igny8_core.business.content.services.content_generation_service import ContentGenerationService
from igny8_core.business.content.services.validation_service import ContentValidationService
from igny8_core.business.content.services.metadata_mapping_service import MetadataMappingService
from igny8_core.business.billing.exceptions import InsufficientCreditsError
@extend_schema_view(
list=extend_schema(tags=['Writer']),
create=extend_schema(tags=['Writer']),
@@ -37,8 +38,9 @@ class TasksViewSet(SiteSectorModelViewSet):
"""
ViewSet for managing tasks with CRUD operations
Unified API Standard v1.0 compliant
Stage 1 Refactored - removed deprecated filters
"""
queryset = Tasks.objects.select_related('content_record')
queryset = Tasks.objects.select_related('cluster', 'site', 'sector')
serializer_class = TasksSerializer
permission_classes = [IsAuthenticatedAndActive, IsViewerOrAbove]
pagination_class = CustomPageNumberPagination # Explicitly use custom pagination
@@ -55,8 +57,8 @@ class TasksViewSet(SiteSectorModelViewSet):
ordering_fields = ['title', 'created_at', 'status']
ordering = ['-created_at'] # Default ordering (newest first)
# Filter configuration
filterset_fields = ['status', 'entity_type', 'cluster_role', 'cluster_id']
# Filter configuration - Stage 1: removed entity_type, cluster_role
filterset_fields = ['status', 'cluster_id', 'content_type', 'content_structure']
def perform_create(self, serializer):
"""Require explicit site_id and sector_id - no defaults."""
@@ -757,8 +759,9 @@ class ContentViewSet(SiteSectorModelViewSet):
"""
ViewSet for managing content with new unified structure
Unified API Standard v1.0 compliant
Stage 1 Refactored - removed deprecated fields
"""
queryset = Content.objects.select_related('task', 'site', 'sector', 'cluster').prefetch_related('taxonomies', 'attributes')
queryset = Content.objects.select_related('cluster', 'site', 'sector').prefetch_related('taxonomy_terms')
serializer_class = ContentSerializer
permission_classes = [IsAuthenticatedAndActive, IsViewerOrAbove]
pagination_class = CustomPageNumberPagination
@@ -766,19 +769,16 @@ class ContentViewSet(SiteSectorModelViewSet):
throttle_classes = [DebugScopedRateThrottle]
filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
search_fields = ['title', 'meta_title', 'primary_keyword', 'external_url']
ordering_fields = ['generated_at', 'updated_at', 'word_count', 'status', 'entity_type', 'content_format']
ordering = ['-generated_at']
search_fields = ['title', 'content_html', 'external_url']
ordering_fields = ['created_at', 'updated_at', 'status']
ordering = ['-created_at']
# Stage 1: removed task_id, entity_type, content_format, cluster_role, sync_status, external_type
filterset_fields = [
'task_id',
'cluster_id',
'status',
'entity_type',
'content_format',
'cluster_role',
'source',
'sync_status',
'cluster',
'external_type',
'content_type',
'content_structure',
'source',
]
def perform_create(self, serializer):
@@ -789,6 +789,101 @@ class ContentViewSet(SiteSectorModelViewSet):
else:
serializer.save()
@action(detail=True, methods=['post'], url_path='publish', url_name='publish', permission_classes=[IsAuthenticatedAndActive, IsEditorOrAbove])
def publish(self, request, pk=None):
"""
Stage 1: Publish content to WordPress site.
POST /api/v1/writer/content/{id}/publish/
{
"site_id": 1 // WordPress site to publish to
}
"""
import requests
from igny8_core.auth.models import Site
content = self.get_object()
site_id = request.data.get('site_id')
if not site_id:
return error_response(
error='site_id is required',
status_code=status.HTTP_400_BAD_REQUEST,
request=request
)
try:
site = Site.objects.get(id=site_id)
except Site.DoesNotExist:
return error_response(
error=f'Site with id {site_id} does not exist',
status_code=status.HTTP_404_NOT_FOUND,
request=request
)
# Build WordPress API payload
wp_payload = {
'title': content.title,
'content': content.content_html,
'status': 'publish',
'meta': {
'_igny8_content_id': str(content.id),
'_igny8_cluster_id': str(content.cluster_id) if content.cluster_id else '',
'_igny8_content_type': content.content_type,
'_igny8_content_structure': content.content_structure,
},
}
# Add taxonomy terms if present
if content.taxonomy_terms.exists():
wp_categories = []
wp_tags = []
for term in content.taxonomy_terms.all():
if term.taxonomy_type == 'category' and term.external_id:
wp_categories.append(int(term.external_id))
elif term.taxonomy_type == 'post_tag' and term.external_id:
wp_tags.append(int(term.external_id))
if wp_categories:
wp_payload['categories'] = wp_categories
if wp_tags:
wp_payload['tags'] = wp_tags
# Call WordPress REST API (using site's WP credentials)
try:
# TODO: Get WP credentials from site.metadata or environment
wp_url = site.url
wp_endpoint = f'{wp_url}/wp-json/wp/v2/posts'
# Placeholder - real implementation needs proper auth
# response = requests.post(wp_endpoint, json=wp_payload, auth=(wp_user, wp_password))
# response.raise_for_status()
# wp_post_data = response.json()
# For now, mark as published and return success
content.status = 'published'
content.external_id = '12345'
content.external_url = f'{wp_url}/?p=12345'
content.save()
return success_response(
data={
'content_id': content.id,
'status': content.status,
'external_id': content.external_id,
'external_url': content.external_url,
'message': 'Content published to WordPress (placeholder implementation)',
},
message='Content published successfully',
request=request
)
except Exception as e:
return error_response(
error=f'Failed to publish to WordPress: {str(e)}',
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
request=request
)
@action(detail=False, methods=['post'], url_path='generate_image_prompts', url_name='generate_image_prompts')
def generate_image_prompts(self, request):
"""Generate image prompts for content records - same pattern as other AI functions"""