Section 3-8 - #MIgration Runs -

Multiple Migfeat: Update publishing terminology and add publishing settings

- Changed references from "WordPress" to "Site" across multiple components for consistency.
- Introduced a new "Publishing" tab in Site Settings to manage automatic content approval and publishing behavior.
- Added publishing settings model to the backend with fields for auto-approval, auto-publish, and publishing limits.
- Implemented Celery tasks for scheduling and processing automated content publishing.
- Enhanced Writer Dashboard to include metrics for content published to the site and scheduled for publishing.
This commit is contained in:
IGNY8 VPS (Salman)
2026-01-01 07:10:03 +00:00
parent f81fffc9a6
commit 0340016932
21 changed files with 1200 additions and 36 deletions

View File

@@ -1476,11 +1476,36 @@ class AutomationService:
This stage automatically approves content in 'review' status and
marks it as 'approved' (ready for publishing to WordPress).
Respects PublishingSettings:
- If auto_approval_enabled is False, skip approval and keep content in 'review'
"""
stage_number = 7
stage_name = "Review → Approved"
start_time = time.time()
# Check publishing settings for auto-approval
from igny8_core.business.integration.models import PublishingSettings
publishing_settings, _ = PublishingSettings.get_or_create_for_site(self.site)
if not publishing_settings.auto_approval_enabled:
self.logger.log_stage_progress(
self.run.run_id, self.account.id, self.site.id,
stage_number, "Auto-approval is disabled for this site - skipping Stage 7"
)
self.run.stage_7_result = {
'ready_for_review': 0,
'approved_count': 0,
'content_ids': [],
'skipped': True,
'reason': 'auto_approval_disabled'
}
self.run.status = 'completed'
self.run.completed_at = datetime.now()
self.run.save()
cache.delete(f'automation_lock_{self.site.id}')
return
# Query content ready for review
ready_for_review = Content.objects.filter(
site=self.site,
@@ -1602,13 +1627,55 @@ class AutomationService:
stage_number, approved_count, time_elapsed, 0
)
# Check if auto-publish is enabled and queue approved content for publishing
published_count = 0
if publishing_settings.auto_publish_enabled and approved_count > 0:
self.logger.log_stage_progress(
self.run.run_id, self.account.id, self.site.id,
stage_number, f"Auto-publish enabled - queuing {len(content_ids)} content items for publishing"
)
# Get WordPress integration for this site
from igny8_core.business.integration.models import SiteIntegration
wp_integration = SiteIntegration.objects.filter(
site=self.site,
platform='wordpress',
is_active=True
).first()
if wp_integration:
from igny8_core.tasks.wordpress_publishing import publish_content_to_wordpress
for content_id in content_ids:
try:
# Queue publish task
publish_content_to_wordpress.delay(
content_id=content_id,
site_integration_id=wp_integration.id
)
published_count += 1
except Exception as e:
logger.error(f"[AutomationService] Failed to queue publish for content {content_id}: {str(e)}")
self.logger.log_stage_progress(
self.run.run_id, self.account.id, self.site.id,
stage_number, f"Queued {published_count} content items for WordPress publishing"
)
else:
self.logger.log_stage_progress(
self.run.run_id, self.account.id, self.site.id,
stage_number, "No active WordPress integration found - skipping auto-publish"
)
self.run.stage_7_result = {
'ready_for_review': total_count,
'review_total': total_count,
'approved_count': approved_count,
'content_ids': content_ids,
'time_elapsed': time_elapsed,
'in_progress': False
'in_progress': False,
'auto_published_count': published_count if publishing_settings.auto_publish_enabled else 0,
'auto_publish_enabled': publishing_settings.auto_publish_enabled,
}
self.run.status = 'completed'
self.run.completed_at = datetime.now()
@@ -1617,7 +1684,8 @@ class AutomationService:
# Release lock
cache.delete(f'automation_lock_{self.site.id}')
logger.info(f"[AutomationService] Stage 7 complete: {approved_count} content pieces approved (ready for publishing)")
logger.info(f"[AutomationService] Stage 7 complete: {approved_count} content pieces approved" +
(f", {published_count} queued for publishing" if published_count > 0 else " (ready for publishing)"))
def pause_automation(self):
"""Pause current automation run"""

View File

@@ -282,6 +282,33 @@ class Content(SoftDeletableModel, SiteSectorBaseModel):
help_text="Content status"
)
# Publishing scheduler fields
SITE_STATUS_CHOICES = [
('not_published', 'Not Published'),
('scheduled', 'Scheduled'),
('publishing', 'Publishing'),
('published', 'Published'),
('failed', 'Failed'),
]
site_status = models.CharField(
max_length=50,
choices=SITE_STATUS_CHOICES,
default='not_published',
db_index=True,
help_text="External site publishing status"
)
scheduled_publish_at = models.DateTimeField(
null=True,
blank=True,
db_index=True,
help_text="Scheduled time for publishing to external site"
)
site_status_updated_at = models.DateTimeField(
null=True,
blank=True,
help_text="Last time site_status was changed"
)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)

View File

@@ -0,0 +1,38 @@
# Generated by Django 5.2.9 on 2026-01-01 06:37
import django.core.validators
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('igny8_core_auth', '0018_add_country_remove_intent_seedkeyword'),
('integration', '0002_add_sync_event_model'),
]
operations = [
migrations.CreateModel(
name='PublishingSettings',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('auto_approval_enabled', models.BooleanField(default=True, help_text="Automatically approve content after review (moves to 'approved' status)")),
('auto_publish_enabled', models.BooleanField(default=True, help_text='Automatically publish approved content to the external site')),
('daily_publish_limit', models.PositiveIntegerField(default=3, help_text='Maximum number of articles to publish per day', validators=[django.core.validators.MinValueValidator(1)])),
('weekly_publish_limit', models.PositiveIntegerField(default=15, help_text='Maximum number of articles to publish per week', validators=[django.core.validators.MinValueValidator(1)])),
('monthly_publish_limit', models.PositiveIntegerField(default=50, help_text='Maximum number of articles to publish per month', validators=[django.core.validators.MinValueValidator(1)])),
('publish_days', models.JSONField(default=list, help_text='Days of the week to publish (mon, tue, wed, thu, fri, sat, sun)')),
('publish_time_slots', models.JSONField(default=list, help_text="Times of day to publish (HH:MM format, e.g., ['09:00', '14:00', '18:00'])")),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('account', models.ForeignKey(db_column='tenant_id', on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_set', to='igny8_core_auth.account')),
('site', models.OneToOneField(help_text='Site these publishing settings belong to', on_delete=django.db.models.deletion.CASCADE, related_name='publishing_settings', to='igny8_core_auth.site')),
],
options={
'verbose_name': 'Publishing Settings',
'verbose_name_plural': 'Publishing Settings',
'db_table': 'igny8_publishing_settings',
},
),
]

View File

@@ -244,3 +244,100 @@ class SyncEvent(AccountBaseModel):
def __str__(self):
return f"{self.get_event_type_display()} - {self.description[:50]}"
class PublishingSettings(AccountBaseModel):
"""
Site-level publishing configuration settings.
Controls automatic approval, publishing limits, and scheduling.
"""
DEFAULT_PUBLISH_DAYS = ['mon', 'tue', 'wed', 'thu', 'fri']
DEFAULT_TIME_SLOTS = ['09:00', '14:00', '18:00']
site = models.OneToOneField(
'igny8_core_auth.Site',
on_delete=models.CASCADE,
related_name='publishing_settings',
help_text="Site these publishing settings belong to"
)
# Auto-approval settings
auto_approval_enabled = models.BooleanField(
default=True,
help_text="Automatically approve content after review (moves to 'approved' status)"
)
# Auto-publish settings
auto_publish_enabled = models.BooleanField(
default=True,
help_text="Automatically publish approved content to the external site"
)
# Publishing limits
daily_publish_limit = models.PositiveIntegerField(
default=3,
validators=[MinValueValidator(1)],
help_text="Maximum number of articles to publish per day"
)
weekly_publish_limit = models.PositiveIntegerField(
default=15,
validators=[MinValueValidator(1)],
help_text="Maximum number of articles to publish per week"
)
monthly_publish_limit = models.PositiveIntegerField(
default=50,
validators=[MinValueValidator(1)],
help_text="Maximum number of articles to publish per month"
)
# Publishing schedule
publish_days = models.JSONField(
default=list,
help_text="Days of the week to publish (mon, tue, wed, thu, fri, sat, sun)"
)
publish_time_slots = models.JSONField(
default=list,
help_text="Times of day to publish (HH:MM format, e.g., ['09:00', '14:00', '18:00'])"
)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
app_label = 'integration'
db_table = 'igny8_publishing_settings'
verbose_name = 'Publishing Settings'
verbose_name_plural = 'Publishing Settings'
def __str__(self):
return f"Publishing Settings for {self.site.name}"
def save(self, *args, **kwargs):
"""Set defaults for JSON fields if empty"""
if not self.publish_days:
self.publish_days = self.DEFAULT_PUBLISH_DAYS
if not self.publish_time_slots:
self.publish_time_slots = self.DEFAULT_TIME_SLOTS
super().save(*args, **kwargs)
@classmethod
def get_or_create_for_site(cls, site):
"""Get or create publishing settings for a site with defaults"""
settings, created = cls.objects.get_or_create(
site=site,
defaults={
'account': site.account,
'auto_approval_enabled': True,
'auto_publish_enabled': True,
'daily_publish_limit': 3,
'weekly_publish_limit': 15,
'monthly_publish_limit': 50,
'publish_days': cls.DEFAULT_PUBLISH_DAYS,
'publish_time_slots': cls.DEFAULT_TIME_SLOTS,
}
)
return settings, created