pubslihign and scheduling plan updated

This commit is contained in:
IGNY8 VPS (Salman)
2026-01-16 13:28:24 +00:00
parent 22f7bdaa23
commit 79bdf52856
4 changed files with 1902 additions and 85 deletions

View File

@@ -1518,6 +1518,77 @@ class ContentViewSet(SiteSectorModelViewSet):
request=request
)
@action(detail=True, methods=['post'], url_path='reschedule', url_name='reschedule', permission_classes=[IsAuthenticatedAndActive, IsEditorOrAbove])
def reschedule(self, request, pk=None):
"""
Reschedule failed or published content for republishing.
Updates scheduled_publish_at and sets site_status back to 'scheduled'.
POST /api/v1/writer/content/{id}/reschedule/
{
"scheduled_at": "2026-01-20T14:00:00Z" // ISO 8601 datetime
}
"""
from django.utils import timezone
from dateutil import parser
import logging
logger = logging.getLogger(__name__)
content = self.get_object()
# Get scheduled time from request
scheduled_at_str = request.data.get('scheduled_at')
if not scheduled_at_str:
return error_response(
error='scheduled_at is required (ISO 8601 datetime)',
status_code=status.HTTP_400_BAD_REQUEST,
request=request
)
# Parse datetime
try:
scheduled_at = parser.isoparse(scheduled_at_str)
if scheduled_at.tzinfo is None:
scheduled_at = timezone.make_aware(scheduled_at)
except (ValueError, TypeError) as e:
return error_response(
error=f'Invalid datetime format: {str(e)}. Use ISO 8601 format.',
status_code=status.HTTP_400_BAD_REQUEST,
request=request
)
# Ensure datetime is in the future
if scheduled_at <= timezone.now():
return error_response(
error='Scheduled time must be in the future',
status_code=status.HTTP_400_BAD_REQUEST,
request=request
)
# Store old values for logging
old_status = content.site_status
old_scheduled_at = content.scheduled_publish_at
# Update content - allow rescheduling from any state
content.site_status = 'scheduled'
content.scheduled_publish_at = scheduled_at
content.site_status_updated_at = timezone.now()
content.save(update_fields=['site_status', 'scheduled_publish_at', 'site_status_updated_at', 'updated_at'])
logger.info(f"[ContentViewSet.reschedule] Content {content.id} rescheduled from {old_status} (was {old_scheduled_at}) to {scheduled_at}")
return success_response(
data={
'content_id': content.id,
'site_status': content.site_status,
'scheduled_publish_at': content.scheduled_publish_at.isoformat(),
'previous_status': old_status,
'was_scheduled_for': old_scheduled_at.isoformat() if old_scheduled_at else None,
},
message=f'Content rescheduled for {scheduled_at.strftime("%Y-%m-%d %H:%M")}',
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"""

View File

@@ -232,14 +232,15 @@ def process_scheduled_publications() -> Dict[str, Any]:
- site_status = 'scheduled'
- scheduled_publish_at <= now
For each, triggers the WordPress publishing task.
For each, triggers publishing via PublisherService (current system).
UPDATED: Uses Site.wp_api_key directly (no SiteIntegration needed).
Returns:
Dict with processing results
"""
from igny8_core.business.content.models import Content
from igny8_core.business.integration.models import SiteIntegration
from igny8_core.tasks.wordpress_publishing import publish_content_to_wordpress
from igny8_core.business.publishing.services.publisher_service import PublisherService
results = {
'processed': 0,
@@ -249,33 +250,25 @@ def process_scheduled_publications() -> Dict[str, Any]:
}
now = timezone.now()
publisher_service = PublisherService()
try:
# Get all scheduled content that's due
due_content = Content.objects.filter(
site_status='scheduled',
scheduled_publish_at__lte=now
).select_related('site', 'sector', 'cluster')
).select_related('site', 'sector', 'cluster', 'account')
logger.info(f"[process_scheduled_publications] Found {due_content.count()} content items due for publishing")
for content in due_content:
results['processed'] += 1
try:
# Update status to publishing
content.site_status = 'publishing'
content.site_status_updated_at = timezone.now()
content.save(update_fields=['site_status', 'site_status_updated_at'])
# Get site integration
site_integration = SiteIntegration.objects.filter(
site=content.site,
platform='wordpress',
is_active=True
).first()
if not site_integration:
error_msg = f"No active WordPress integration for site {content.site_id}"
logger.error(error_msg)
# Validate prerequisites
if not content.site:
error_msg = f"Content {content.id} has no site assigned"
logger.error(f"[process_scheduled_publications] {error_msg}")
content.site_status = 'failed'
content.site_status_updated_at = timezone.now()
content.save(update_fields=['site_status', 'site_status_updated_at'])
@@ -283,30 +276,67 @@ def process_scheduled_publications() -> Dict[str, Any]:
results['errors'].append(error_msg)
continue
# Queue the WordPress publishing task
publish_content_to_wordpress.delay(
# Check WordPress configuration on Site
if not content.site.wp_api_key:
error_msg = f"Site '{content.site.name}' (ID: {content.site.id}) has no WordPress API key configured"
logger.error(f"[process_scheduled_publications] {error_msg}")
content.site_status = 'failed'
content.site_status_updated_at = timezone.now()
content.save(update_fields=['site_status', 'site_status_updated_at'])
results['failed'] += 1
results['errors'].append(error_msg)
continue
if not content.site.domain:
error_msg = f"Site '{content.site.name}' (ID: {content.site.id}) has no domain configured"
logger.error(f"[process_scheduled_publications] {error_msg}")
content.site_status = 'failed'
content.site_status_updated_at = timezone.now()
content.save(update_fields=['site_status', 'site_status_updated_at'])
results['failed'] += 1
results['errors'].append(error_msg)
continue
# Update status to publishing
content.site_status = 'publishing'
content.site_status_updated_at = timezone.now()
content.save(update_fields=['site_status', 'site_status_updated_at'])
# Publish via PublisherService (current system)
logger.info(f"[process_scheduled_publications] Publishing content {content.id} '{content.title}' to {content.site.domain}")
publish_result = publisher_service.publish_content(
content_id=content.id,
site_integration_id=site_integration.id
destinations=['wordpress'],
account=content.account
)
logger.info(f"Queued content {content.id} for WordPress publishing")
results['published'] += 1
if publish_result.get('success'):
logger.info(f"[process_scheduled_publications] ✅ Successfully published content {content.id}")
results['published'] += 1
else:
error_msg = f"Publishing failed for content {content.id}: {publish_result.get('error', 'Unknown error')}"
logger.error(f"[process_scheduled_publications] ❌ {error_msg}")
content.site_status = 'failed'
content.site_status_updated_at = timezone.now()
content.save(update_fields=['site_status', 'site_status_updated_at'])
results['failed'] += 1
results['errors'].append(error_msg)
except Exception as e:
error_msg = f"Error processing content {content.id}: {str(e)}"
logger.error(error_msg)
logger.error(f"[process_scheduled_publications] ❌ {error_msg}", exc_info=True)
content.site_status = 'failed'
content.site_status_updated_at = timezone.now()
content.save(update_fields=['site_status', 'site_status_updated_at'])
results['failed'] += 1
results['errors'].append(error_msg)
logger.info(f"Processing completed: {results['published']}/{results['processed']} published successfully")
logger.info(f"[process_scheduled_publications] ✅ Completed: {results['published']}/{results['processed']} published successfully, {results['failed']} failed")
return results
except Exception as e:
error_msg = f"Fatal error in process_scheduled_publications: {str(e)}"
logger.error(error_msg)
logger.error(f"[process_scheduled_publications] ❌ {error_msg}", exc_info=True)
results['errors'].append(error_msg)
return results