AI AUtomtaion, Schudelign and publishign fromt and backe end refoactr
This commit is contained in:
@@ -68,7 +68,23 @@ def schedule_approved_content() -> Dict[str, Any]:
|
||||
results['sites_processed'] += 1
|
||||
continue
|
||||
|
||||
# Calculate available slots
|
||||
# Handle immediate mode - schedule for now (will be picked up by process_scheduled_publications)
|
||||
if settings.scheduling_mode == 'immediate':
|
||||
for content in pending_content:
|
||||
content.scheduled_publish_at = timezone.now()
|
||||
content.site_status = 'scheduled'
|
||||
content.site_status_updated_at = timezone.now()
|
||||
content.save(update_fields=['scheduled_publish_at', 'site_status', 'site_status_updated_at'])
|
||||
|
||||
site_result['scheduled_count'] += 1
|
||||
results['content_scheduled'] += 1
|
||||
logger.info(f"Scheduled content {content.id} for immediate publishing")
|
||||
|
||||
results['details'].append(site_result)
|
||||
results['sites_processed'] += 1
|
||||
continue
|
||||
|
||||
# Calculate available slots for time_slots and stagger modes
|
||||
available_slots = _calculate_available_slots(settings, site)
|
||||
|
||||
# Assign slots to content
|
||||
@@ -110,6 +126,11 @@ def _calculate_available_slots(settings: 'PublishingSettings', site: 'Site') ->
|
||||
"""
|
||||
Calculate available publishing time slots based on settings and limits.
|
||||
|
||||
Supports three scheduling modes:
|
||||
- time_slots: Publish at specific configured times each day
|
||||
- stagger: Spread evenly throughout publish hours
|
||||
- immediate: No scheduling - return immediately (handled separately)
|
||||
|
||||
Args:
|
||||
settings: PublishingSettings instance
|
||||
site: Site instance
|
||||
@@ -120,13 +141,13 @@ def _calculate_available_slots(settings: 'PublishingSettings', site: 'Site') ->
|
||||
from igny8_core.business.content.models import Content
|
||||
|
||||
now = timezone.now()
|
||||
slots = []
|
||||
|
||||
# Get configured days and times
|
||||
# Immediate mode - return empty list (content published immediately in process_scheduled_publications)
|
||||
if settings.scheduling_mode == 'immediate':
|
||||
return []
|
||||
|
||||
# Common setup
|
||||
publish_days = settings.publish_days or ['mon', 'tue', 'wed', 'thu', 'fri']
|
||||
publish_times = settings.publish_time_slots or ['09:00', '14:00', '18:00']
|
||||
|
||||
# Day name mapping
|
||||
day_map = {
|
||||
'mon': 0, 'tue': 1, 'wed': 2, 'thu': 3,
|
||||
'fri': 4, 'sat': 5, 'sun': 6
|
||||
@@ -134,22 +155,11 @@ def _calculate_available_slots(settings: 'PublishingSettings', site: 'Site') ->
|
||||
allowed_days = [day_map.get(d.lower(), -1) for d in publish_days]
|
||||
allowed_days = [d for d in allowed_days if d >= 0]
|
||||
|
||||
# Parse time slots
|
||||
time_slots = []
|
||||
for time_str in publish_times:
|
||||
try:
|
||||
hour, minute = map(int, time_str.split(':'))
|
||||
time_slots.append((hour, minute))
|
||||
except (ValueError, AttributeError):
|
||||
continue
|
||||
|
||||
if not time_slots:
|
||||
time_slots = [(9, 0), (14, 0), (18, 0)]
|
||||
|
||||
# Calculate limits
|
||||
daily_limit = settings.daily_publish_limit
|
||||
weekly_limit = settings.weekly_publish_limit
|
||||
monthly_limit = settings.monthly_publish_limit
|
||||
queue_limit = getattr(settings, 'queue_limit', 100) or 100
|
||||
|
||||
# Count existing scheduled/published content
|
||||
today_start = now.replace(hour=0, minute=0, second=0, microsecond=0)
|
||||
@@ -174,14 +184,53 @@ def _calculate_available_slots(settings: 'PublishingSettings', site: 'Site') ->
|
||||
scheduled_publish_at__gte=month_start
|
||||
).count()
|
||||
|
||||
# Generate slots for next 30 days
|
||||
current_date = now.date()
|
||||
slots_per_day = {} # Track slots used per day
|
||||
# Route to appropriate slot generator
|
||||
if settings.scheduling_mode == 'stagger':
|
||||
return _generate_stagger_slots(
|
||||
settings, site, now, allowed_days,
|
||||
daily_limit, weekly_limit, monthly_limit, queue_limit,
|
||||
daily_count, weekly_count, monthly_count
|
||||
)
|
||||
else:
|
||||
# Default to time_slots mode
|
||||
return _generate_time_slot_slots(
|
||||
settings, site, now, allowed_days,
|
||||
daily_limit, weekly_limit, monthly_limit, queue_limit,
|
||||
daily_count, weekly_count, monthly_count
|
||||
)
|
||||
|
||||
|
||||
def _generate_time_slot_slots(
|
||||
settings, site, now, allowed_days,
|
||||
daily_limit, weekly_limit, monthly_limit, queue_limit,
|
||||
daily_count, weekly_count, monthly_count
|
||||
) -> list:
|
||||
"""Generate slots based on specific time slots (original mode)."""
|
||||
from igny8_core.business.content.models import Content
|
||||
|
||||
for day_offset in range(30):
|
||||
slots = []
|
||||
publish_times = settings.publish_time_slots or ['09:00', '14:00', '18:00']
|
||||
|
||||
# Parse time slots
|
||||
time_slots = []
|
||||
for time_str in publish_times:
|
||||
try:
|
||||
hour, minute = map(int, time_str.split(':'))
|
||||
time_slots.append((hour, minute))
|
||||
except (ValueError, AttributeError):
|
||||
continue
|
||||
|
||||
if not time_slots:
|
||||
time_slots = [(9, 0), (14, 0), (18, 0)]
|
||||
|
||||
current_date = now.date()
|
||||
slots_per_day = {}
|
||||
today_start = now.replace(hour=0, minute=0, second=0, microsecond=0)
|
||||
week_start = today_start - timedelta(days=now.weekday())
|
||||
|
||||
for day_offset in range(90): # Look 90 days ahead
|
||||
check_date = current_date + timedelta(days=day_offset)
|
||||
|
||||
# Check if day is allowed
|
||||
if check_date.weekday() not in allowed_days:
|
||||
continue
|
||||
|
||||
@@ -216,8 +265,111 @@ def _calculate_available_slots(settings: 'PublishingSettings', site: 'Site') ->
|
||||
slots.append(slot_time)
|
||||
slots_per_day[day_key] = slots_per_day.get(day_key, 0) + 1
|
||||
|
||||
# Limit total slots to prevent memory issues
|
||||
if len(slots) >= 100:
|
||||
# Respect queue limit
|
||||
if len(slots) >= queue_limit:
|
||||
return slots
|
||||
|
||||
return slots
|
||||
|
||||
|
||||
def _generate_stagger_slots(
|
||||
settings, site, now, allowed_days,
|
||||
daily_limit, weekly_limit, monthly_limit, queue_limit,
|
||||
daily_count, weekly_count, monthly_count
|
||||
) -> list:
|
||||
"""
|
||||
Generate slots spread evenly throughout the publishing window.
|
||||
|
||||
Distributes content throughout the day based on stagger_start_time,
|
||||
stagger_end_time, and stagger_interval_minutes.
|
||||
"""
|
||||
from igny8_core.business.content.models import Content
|
||||
|
||||
slots = []
|
||||
|
||||
# Get stagger settings with defaults
|
||||
start_hour, start_minute = 9, 0
|
||||
end_hour, end_minute = 18, 0
|
||||
|
||||
if hasattr(settings, 'stagger_start_time') and settings.stagger_start_time:
|
||||
start_hour = settings.stagger_start_time.hour
|
||||
start_minute = settings.stagger_start_time.minute
|
||||
|
||||
if hasattr(settings, 'stagger_end_time') and settings.stagger_end_time:
|
||||
end_hour = settings.stagger_end_time.hour
|
||||
end_minute = settings.stagger_end_time.minute
|
||||
|
||||
interval_minutes = getattr(settings, 'stagger_interval_minutes', 30) or 30
|
||||
interval = timedelta(minutes=interval_minutes)
|
||||
|
||||
current_date = now.date()
|
||||
slots_per_day = {}
|
||||
today_start = now.replace(hour=0, minute=0, second=0, microsecond=0)
|
||||
week_start = today_start - timedelta(days=now.weekday())
|
||||
|
||||
for day_offset in range(90): # Look 90 days ahead
|
||||
check_date = current_date + timedelta(days=day_offset)
|
||||
|
||||
if check_date.weekday() not in allowed_days:
|
||||
continue
|
||||
|
||||
# Day's publishing window
|
||||
day_start = timezone.make_aware(
|
||||
datetime.combine(check_date, datetime.min.time().replace(hour=start_hour, minute=start_minute))
|
||||
)
|
||||
day_end = timezone.make_aware(
|
||||
datetime.combine(check_date, datetime.min.time().replace(hour=end_hour, minute=end_minute))
|
||||
)
|
||||
|
||||
# Get existing scheduled times for this day to avoid conflicts
|
||||
existing_times = set(
|
||||
Content.objects.filter(
|
||||
site=site,
|
||||
site_status='scheduled',
|
||||
scheduled_publish_at__date=check_date
|
||||
).values_list('scheduled_publish_at', flat=True)
|
||||
)
|
||||
|
||||
# Start slot calculation
|
||||
current_slot = day_start
|
||||
if check_date == current_date and now > day_start:
|
||||
# Start from next interval after now
|
||||
minutes_since_start = (now - day_start).total_seconds() / 60
|
||||
intervals_passed = int(minutes_since_start / interval_minutes) + 1
|
||||
current_slot = day_start + timedelta(minutes=intervals_passed * interval_minutes)
|
||||
|
||||
day_key = check_date.isoformat()
|
||||
|
||||
while current_slot <= day_end:
|
||||
# Check daily limit
|
||||
slots_this_day = slots_per_day.get(day_key, 0)
|
||||
if daily_limit and (daily_count + slots_this_day) >= daily_limit:
|
||||
break # Move to next day
|
||||
|
||||
# Check weekly limit
|
||||
slot_week_start = current_slot - timedelta(days=current_slot.weekday())
|
||||
if slot_week_start.date() == week_start.date():
|
||||
scheduled_in_week = weekly_count + len([s for s in slots if s >= week_start])
|
||||
if weekly_limit and scheduled_in_week >= weekly_limit:
|
||||
current_slot += interval
|
||||
continue
|
||||
|
||||
# Check monthly limit
|
||||
if current_slot.month == now.month and current_slot.year == now.year:
|
||||
scheduled_in_month = monthly_count + len([s for s in slots if s.month == now.month])
|
||||
if monthly_limit and scheduled_in_month >= monthly_limit:
|
||||
current_slot += interval
|
||||
continue
|
||||
|
||||
# Avoid existing scheduled times
|
||||
if current_slot not in existing_times:
|
||||
slots.append(current_slot)
|
||||
slots_per_day[day_key] = slots_per_day.get(day_key, 0) + 1
|
||||
|
||||
current_slot += interval
|
||||
|
||||
# Respect queue limit
|
||||
if len(slots) >= queue_limit:
|
||||
return slots
|
||||
|
||||
return slots
|
||||
|
||||
Reference in New Issue
Block a user