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:
@@ -5,7 +5,7 @@ Phase 6: Site Integration & Multi-Destination Publishing
|
||||
from django.urls import path, include
|
||||
from rest_framework.routers import DefaultRouter
|
||||
|
||||
from igny8_core.modules.integration.views import IntegrationViewSet
|
||||
from igny8_core.modules.integration.views import IntegrationViewSet, PublishingSettingsViewSet
|
||||
from igny8_core.modules.integration.webhooks import (
|
||||
wordpress_status_webhook,
|
||||
wordpress_metadata_webhook,
|
||||
@@ -14,9 +14,19 @@ from igny8_core.modules.integration.webhooks import (
|
||||
router = DefaultRouter()
|
||||
router.register(r'integrations', IntegrationViewSet, basename='integration')
|
||||
|
||||
# Create PublishingSettings ViewSet instance
|
||||
publishing_settings_viewset = PublishingSettingsViewSet.as_view({
|
||||
'get': 'retrieve',
|
||||
'put': 'update',
|
||||
'patch': 'partial_update',
|
||||
})
|
||||
|
||||
urlpatterns = [
|
||||
path('', include(router.urls)),
|
||||
|
||||
# Site-level publishing settings
|
||||
path('sites/<int:site_id>/publishing-settings/', publishing_settings_viewset, name='publishing-settings'),
|
||||
|
||||
# Webhook endpoints
|
||||
path('webhooks/wordpress/status/', wordpress_status_webhook, name='wordpress-status-webhook'),
|
||||
path('webhooks/wordpress/metadata/', wordpress_metadata_webhook, name='wordpress-metadata-webhook'),
|
||||
|
||||
@@ -838,5 +838,148 @@ class IntegrationViewSet(SiteSectorModelViewSet):
|
||||
}, request=request)
|
||||
|
||||
|
||||
# PublishingSettings ViewSet
|
||||
from rest_framework import serializers, viewsets
|
||||
from igny8_core.business.integration.models import PublishingSettings
|
||||
|
||||
|
||||
class PublishingSettingsSerializer(serializers.ModelSerializer):
|
||||
"""Serializer for PublishingSettings model"""
|
||||
|
||||
class Meta:
|
||||
model = PublishingSettings
|
||||
fields = [
|
||||
'id',
|
||||
'site',
|
||||
'auto_approval_enabled',
|
||||
'auto_publish_enabled',
|
||||
'daily_publish_limit',
|
||||
'weekly_publish_limit',
|
||||
'monthly_publish_limit',
|
||||
'publish_days',
|
||||
'publish_time_slots',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
]
|
||||
read_only_fields = ['id', 'site', 'created_at', 'updated_at']
|
||||
|
||||
|
||||
@extend_schema_view(
|
||||
retrieve=extend_schema(tags=['Integration']),
|
||||
update=extend_schema(tags=['Integration']),
|
||||
partial_update=extend_schema(tags=['Integration']),
|
||||
)
|
||||
class PublishingSettingsViewSet(viewsets.ViewSet):
|
||||
"""
|
||||
ViewSet for managing site-level publishing settings.
|
||||
|
||||
GET /api/v1/integration/sites/{site_id}/publishing-settings/
|
||||
PUT /api/v1/integration/sites/{site_id}/publishing-settings/
|
||||
PATCH /api/v1/integration/sites/{site_id}/publishing-settings/
|
||||
"""
|
||||
permission_classes = [IsAuthenticatedAndActive, IsEditorOrAbove]
|
||||
throttle_scope = 'integration'
|
||||
throttle_classes = [DebugScopedRateThrottle]
|
||||
|
||||
def _get_site(self, site_id, request):
|
||||
"""Get site and verify user has access"""
|
||||
from igny8_core.auth.models import Site
|
||||
try:
|
||||
site = Site.objects.get(id=int(site_id))
|
||||
# Check if user has access to this site (same account)
|
||||
if hasattr(request, 'account') and site.account != request.account:
|
||||
return None
|
||||
return site
|
||||
except (Site.DoesNotExist, ValueError, TypeError):
|
||||
return None
|
||||
|
||||
@extend_schema(tags=['Integration'])
|
||||
def retrieve(self, request, site_id=None):
|
||||
"""
|
||||
Get publishing settings for a site.
|
||||
Creates default settings if they don't exist.
|
||||
"""
|
||||
site = self._get_site(site_id, request)
|
||||
if not site:
|
||||
return error_response(
|
||||
'Site not found or access denied',
|
||||
None,
|
||||
status.HTTP_404_NOT_FOUND,
|
||||
request
|
||||
)
|
||||
|
||||
# Get or create settings with defaults
|
||||
settings, created = PublishingSettings.get_or_create_for_site(site)
|
||||
|
||||
serializer = PublishingSettingsSerializer(settings)
|
||||
return success_response(
|
||||
data=serializer.data,
|
||||
message='Publishing settings retrieved' + (' (created with defaults)' if created else ''),
|
||||
request=request
|
||||
)
|
||||
|
||||
@extend_schema(tags=['Integration'])
|
||||
def update(self, request, site_id=None):
|
||||
"""
|
||||
Update publishing settings for a site (full update).
|
||||
"""
|
||||
site = self._get_site(site_id, request)
|
||||
if not site:
|
||||
return error_response(
|
||||
'Site not found or access denied',
|
||||
None,
|
||||
status.HTTP_404_NOT_FOUND,
|
||||
request
|
||||
)
|
||||
|
||||
# Get or create settings
|
||||
settings, _ = PublishingSettings.get_or_create_for_site(site)
|
||||
|
||||
serializer = PublishingSettingsSerializer(settings, data=request.data)
|
||||
if serializer.is_valid():
|
||||
serializer.save()
|
||||
return success_response(
|
||||
data=serializer.data,
|
||||
message='Publishing settings updated',
|
||||
request=request
|
||||
)
|
||||
|
||||
return error_response(
|
||||
'Validation failed',
|
||||
serializer.errors,
|
||||
status.HTTP_400_BAD_REQUEST,
|
||||
request
|
||||
)
|
||||
|
||||
@extend_schema(tags=['Integration'])
|
||||
def partial_update(self, request, site_id=None):
|
||||
"""
|
||||
Partially update publishing settings for a site.
|
||||
"""
|
||||
site = self._get_site(site_id, request)
|
||||
if not site:
|
||||
return error_response(
|
||||
'Site not found or access denied',
|
||||
None,
|
||||
status.HTTP_404_NOT_FOUND,
|
||||
request
|
||||
)
|
||||
|
||||
# Get or create settings
|
||||
settings, _ = PublishingSettings.get_or_create_for_site(site)
|
||||
|
||||
serializer = PublishingSettingsSerializer(settings, data=request.data, partial=True)
|
||||
if serializer.is_valid():
|
||||
serializer.save()
|
||||
return success_response(
|
||||
data=serializer.data,
|
||||
message='Publishing settings updated',
|
||||
request=request
|
||||
)
|
||||
|
||||
return error_response(
|
||||
'Validation failed',
|
||||
serializer.errors,
|
||||
status.HTTP_400_BAD_REQUEST,
|
||||
request
|
||||
)
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
# Generated migration for publishing scheduler fields
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('writer', '0014_add_approved_status'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='content',
|
||||
name='site_status',
|
||||
field=models.CharField(
|
||||
choices=[
|
||||
('not_published', 'Not Published'),
|
||||
('scheduled', 'Scheduled'),
|
||||
('publishing', 'Publishing'),
|
||||
('published', 'Published'),
|
||||
('failed', 'Failed'),
|
||||
],
|
||||
db_index=True,
|
||||
default='not_published',
|
||||
help_text='External site publishing status',
|
||||
max_length=50,
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='content',
|
||||
name='scheduled_publish_at',
|
||||
field=models.DateTimeField(
|
||||
blank=True,
|
||||
db_index=True,
|
||||
help_text='Scheduled time for publishing to external site',
|
||||
null=True,
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='content',
|
||||
name='site_status_updated_at',
|
||||
field=models.DateTimeField(
|
||||
blank=True,
|
||||
help_text='Last time site_status was changed',
|
||||
null=True,
|
||||
),
|
||||
),
|
||||
]
|
||||
@@ -186,6 +186,9 @@ class ContentSerializer(serializers.ModelSerializer):
|
||||
'external_url',
|
||||
'source',
|
||||
'status',
|
||||
'site_status',
|
||||
'scheduled_publish_at',
|
||||
'site_status_updated_at',
|
||||
'word_count',
|
||||
'sector_name',
|
||||
'site_id',
|
||||
@@ -197,7 +200,7 @@ class ContentSerializer(serializers.ModelSerializer):
|
||||
'created_at',
|
||||
'updated_at',
|
||||
]
|
||||
read_only_fields = ['id', 'created_at', 'updated_at', 'account_id']
|
||||
read_only_fields = ['id', 'created_at', 'updated_at', 'account_id', 'site_status', 'scheduled_publish_at', 'site_status_updated_at']
|
||||
|
||||
def validate(self, attrs):
|
||||
"""Ensure required fields for Content creation"""
|
||||
|
||||
Reference in New Issue
Block a user