Phase 1-3 Implemented - PUBLISHING-PROGRESS-AND-SCHEDULING-UX-PLAN

This commit is contained in:
IGNY8 VPS (Salman)
2026-01-16 14:14:17 +00:00
parent e704ed8bcf
commit 1f0a31fe79
14 changed files with 1809 additions and 224 deletions

View File

@@ -1589,6 +1589,207 @@ class ContentViewSet(SiteSectorModelViewSet):
request=request
)
@action(detail=False, methods=['post'], url_path='bulk_schedule_preview', url_name='bulk_schedule_preview', permission_classes=[IsAuthenticatedAndActive, IsEditorOrAbove])
def bulk_schedule_preview(self, request):
"""
Preview bulk scheduling with site default settings.
Shows what the schedule would look like before confirming.
POST /api/v1/writer/content/bulk_schedule_preview/
{
"content_ids": [123, 124, 125],
"site_id": 45
}
"""
from django.utils import timezone
from datetime import timedelta
from igny8_core.business.integration.models import Site, SitePublishingSettings
import logging
logger = logging.getLogger(__name__)
content_ids = request.data.get('content_ids', [])
site_id = request.data.get('site_id')
if not content_ids:
return error_response(
error='content_ids is required',
status_code=status.HTTP_400_BAD_REQUEST,
request=request
)
if not site_id:
return error_response(
error='site_id is required',
status_code=status.HTTP_400_BAD_REQUEST,
request=request
)
# Get site and publishing settings
try:
site = Site.objects.get(id=site_id)
pub_settings = SitePublishingSettings.objects.filter(site=site).first()
except Site.DoesNotExist:
return error_response(
error=f'Site {site_id} not found',
status_code=status.HTTP_404_NOT_FOUND,
request=request
)
# Default settings if none exist
base_time_str = '09:00 AM'
stagger_interval = 15 # minutes
timezone_str = 'America/New_York'
if pub_settings:
base_time_str = pub_settings.auto_publish_time or base_time_str
stagger_interval = pub_settings.stagger_interval_minutes or stagger_interval
timezone_str = pub_settings.timezone or timezone_str
# Get content items
content_qs = self.get_queryset().filter(id__in=content_ids)
# Generate schedule preview
schedule_preview = []
now = timezone.now()
# Parse base time (format: "09:00 AM" or "14:30")
try:
from datetime import datetime
if 'AM' in base_time_str or 'PM' in base_time_str:
time_obj = datetime.strptime(base_time_str, '%I:%M %p').time()
else:
time_obj = datetime.strptime(base_time_str, '%H:%M').time()
except ValueError:
time_obj = datetime.strptime('09:00', '%H:%M').time()
# Start from tomorrow at base time
start_date = now.replace(hour=time_obj.hour, minute=time_obj.minute, second=0, microsecond=0)
if start_date <= now:
start_date += timedelta(days=1)
# Create schedule for each content item
for index, content in enumerate(content_qs):
scheduled_at = start_date + timedelta(minutes=stagger_interval * index)
schedule_preview.append({
'content_id': content.id,
'title': content.title,
'scheduled_at': scheduled_at.isoformat(),
})
logger.info(f"[bulk_schedule_preview] Generated preview for {len(schedule_preview)} items")
return success_response(
data={
'scheduled_count': len(schedule_preview),
'schedule_preview': schedule_preview,
'site_settings': {
'base_time': base_time_str,
'stagger_interval': stagger_interval,
'timezone': timezone_str,
},
},
message=f'Preview generated for {len(schedule_preview)} items',
request=request
)
@action(detail=False, methods=['post'], url_path='bulk_schedule', url_name='bulk_schedule', permission_classes=[IsAuthenticatedAndActive, IsEditorOrAbove])
def bulk_schedule(self, request):
"""
Bulk schedule multiple content items using site default settings.
POST /api/v1/writer/content/bulk_schedule/
{
"content_ids": [123, 124, 125],
"use_site_defaults": true,
"site_id": 45
}
"""
from django.utils import timezone
from datetime import timedelta
from igny8_core.business.integration.models import Site, SitePublishingSettings
import logging
logger = logging.getLogger(__name__)
content_ids = request.data.get('content_ids', [])
use_site_defaults = request.data.get('use_site_defaults', True)
site_id = request.data.get('site_id')
if not content_ids:
return error_response(
error='content_ids is required',
status_code=status.HTTP_400_BAD_REQUEST,
request=request
)
if not site_id:
return error_response(
error='site_id is required',
status_code=status.HTTP_400_BAD_REQUEST,
request=request
)
# Get site and publishing settings
try:
site = Site.objects.get(id=site_id)
pub_settings = SitePublishingSettings.objects.filter(site=site).first()
except Site.DoesNotExist:
return error_response(
error=f'Site {site_id} not found',
status_code=status.HTTP_404_NOT_FOUND,
request=request
)
# Default settings if none exist
base_time_str = '09:00 AM'
stagger_interval = 15 # minutes
if pub_settings and use_site_defaults:
base_time_str = pub_settings.auto_publish_time or base_time_str
stagger_interval = pub_settings.stagger_interval_minutes or stagger_interval
# Get content items
content_qs = self.get_queryset().filter(id__in=content_ids)
# Generate schedule and apply
now = timezone.now()
# Parse base time
try:
from datetime import datetime
if 'AM' in base_time_str or 'PM' in base_time_str:
time_obj = datetime.strptime(base_time_str, '%I:%M %p').time()
else:
time_obj = datetime.strptime(base_time_str, '%H:%M').time()
except ValueError:
time_obj = datetime.strptime('09:00', '%H:%M').time()
# Start from tomorrow at base time
start_date = now.replace(hour=time_obj.hour, minute=time_obj.minute, second=0, microsecond=0)
if start_date <= now:
start_date += timedelta(days=1)
# Schedule each content item
scheduled_count = 0
for index, content in enumerate(content_qs):
scheduled_at = start_date + timedelta(minutes=stagger_interval * index)
content.site_status = 'scheduled'
content.scheduled_publish_at = scheduled_at
content.site_status_updated_at = now
content.save(update_fields=['site_status', 'scheduled_publish_at', 'site_status_updated_at', 'updated_at'])
scheduled_count += 1
logger.info(f"[bulk_schedule] Scheduled {scheduled_count} content items")
return success_response(
data={
'scheduled_count': scheduled_count,
},
message=f'Successfully scheduled {scheduled_count} items',
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"""