Add source tracking and sync status fields to Content model; update services module
- Introduced new fields in the Content model for source tracking and sync status, including external references and optimization fields. - Updated the services module to include new content generation and pipeline services for better organization and clarity.
This commit is contained in:
@@ -115,6 +115,47 @@ class Content(SiteSectorBaseModel):
|
||||
generated_at = models.DateTimeField(auto_now_add=True)
|
||||
updated_at = models.DateTimeField(auto_now=True)
|
||||
|
||||
# Phase 4: Source tracking
|
||||
SOURCE_CHOICES = [
|
||||
('igny8', 'IGNY8 Generated'),
|
||||
('wordpress', 'WordPress Synced'),
|
||||
('shopify', 'Shopify Synced'),
|
||||
('custom', 'Custom API Synced'),
|
||||
]
|
||||
source = models.CharField(
|
||||
max_length=50,
|
||||
choices=SOURCE_CHOICES,
|
||||
default='igny8',
|
||||
db_index=True,
|
||||
help_text="Source of the content"
|
||||
)
|
||||
|
||||
SYNC_STATUS_CHOICES = [
|
||||
('native', 'Native IGNY8 Content'),
|
||||
('imported', 'Imported from External'),
|
||||
('synced', 'Synced from External'),
|
||||
]
|
||||
sync_status = models.CharField(
|
||||
max_length=50,
|
||||
choices=SYNC_STATUS_CHOICES,
|
||||
default='native',
|
||||
db_index=True,
|
||||
help_text="Sync status of the content"
|
||||
)
|
||||
|
||||
# External reference fields
|
||||
external_id = models.CharField(max_length=255, blank=True, null=True, help_text="External platform ID")
|
||||
external_url = models.URLField(blank=True, null=True, help_text="External platform URL")
|
||||
sync_metadata = models.JSONField(default=dict, blank=True, help_text="Platform-specific sync metadata")
|
||||
|
||||
# Phase 4: Linking fields
|
||||
internal_links = models.JSONField(default=list, blank=True, help_text="Internal links added by linker")
|
||||
linker_version = models.IntegerField(default=0, help_text="Version of linker processing")
|
||||
|
||||
# Phase 4: Optimization fields
|
||||
optimizer_version = models.IntegerField(default=0, help_text="Version of optimizer processing")
|
||||
optimization_scores = models.JSONField(default=dict, blank=True, help_text="Optimization scores (SEO, readability, engagement)")
|
||||
|
||||
class Meta:
|
||||
app_label = 'writer'
|
||||
db_table = 'igny8_content'
|
||||
@@ -124,6 +165,9 @@ class Content(SiteSectorBaseModel):
|
||||
indexes = [
|
||||
models.Index(fields=['task']),
|
||||
models.Index(fields=['generated_at']),
|
||||
models.Index(fields=['source']),
|
||||
models.Index(fields=['sync_status']),
|
||||
models.Index(fields=['source', 'sync_status']),
|
||||
]
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
"""
|
||||
Content services
|
||||
Content Services
|
||||
"""
|
||||
from igny8_core.business.content.services.content_generation_service import ContentGenerationService
|
||||
from igny8_core.business.content.services.content_pipeline_service import ContentPipelineService
|
||||
|
||||
__all__ = ['ContentGenerationService', 'ContentPipelineService']
|
||||
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
"""
|
||||
Content Pipeline Service
|
||||
Orchestrates content processing pipeline: Writer → Linker → Optimizer
|
||||
"""
|
||||
import logging
|
||||
from typing import List, Optional
|
||||
from igny8_core.business.content.models import Content
|
||||
from igny8_core.business.linking.services.linker_service import LinkerService
|
||||
from igny8_core.business.optimization.services.optimizer_service import OptimizerService
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ContentPipelineService:
|
||||
"""Orchestrates content processing pipeline"""
|
||||
|
||||
def __init__(self):
|
||||
self.linker_service = LinkerService()
|
||||
self.optimizer_service = OptimizerService()
|
||||
|
||||
def process_writer_content(
|
||||
self,
|
||||
content_id: int,
|
||||
stages: Optional[List[str]] = None
|
||||
) -> Content:
|
||||
"""
|
||||
Writer → Linker → Optimizer pipeline.
|
||||
|
||||
Args:
|
||||
content_id: Content ID from Writer
|
||||
stages: List of stages to run: ['linking', 'optimization'] (default: both)
|
||||
|
||||
Returns:
|
||||
Processed Content instance
|
||||
"""
|
||||
if stages is None:
|
||||
stages = ['linking', 'optimization']
|
||||
|
||||
try:
|
||||
content = Content.objects.get(id=content_id, source='igny8')
|
||||
except Content.DoesNotExist:
|
||||
raise ValueError(f"IGNY8 content with id {content_id} does not exist")
|
||||
|
||||
# Stage 1: Linking
|
||||
if 'linking' in stages:
|
||||
try:
|
||||
content = self.linker_service.process(content.id)
|
||||
logger.info(f"Linked content {content_id}")
|
||||
except Exception as e:
|
||||
logger.error(f"Error in linking stage for content {content_id}: {str(e)}", exc_info=True)
|
||||
# Continue to next stage even if linking fails
|
||||
pass
|
||||
|
||||
# Stage 2: Optimization
|
||||
if 'optimization' in stages:
|
||||
try:
|
||||
content = self.optimizer_service.optimize_from_writer(content.id)
|
||||
logger.info(f"Optimized content {content_id}")
|
||||
except Exception as e:
|
||||
logger.error(f"Error in optimization stage for content {content_id}: {str(e)}", exc_info=True)
|
||||
# Don't fail the whole pipeline
|
||||
pass
|
||||
|
||||
return content
|
||||
|
||||
def process_synced_content(
|
||||
self,
|
||||
content_id: int,
|
||||
stages: Optional[List[str]] = None
|
||||
) -> Content:
|
||||
"""
|
||||
Synced Content → Optimizer pipeline (skip linking if needed).
|
||||
|
||||
Args:
|
||||
content_id: Content ID from sync (WordPress, Shopify, etc.)
|
||||
stages: List of stages to run: ['optimization'] (default: optimization only)
|
||||
|
||||
Returns:
|
||||
Processed Content instance
|
||||
"""
|
||||
if stages is None:
|
||||
stages = ['optimization']
|
||||
|
||||
try:
|
||||
content = Content.objects.get(id=content_id)
|
||||
except Content.DoesNotExist:
|
||||
raise ValueError(f"Content with id {content_id} does not exist")
|
||||
|
||||
# Stage: Optimization (skip linking for synced content by default)
|
||||
if 'optimization' in stages:
|
||||
try:
|
||||
if content.source == 'wordpress':
|
||||
content = self.optimizer_service.optimize_from_wordpress_sync(content.id)
|
||||
elif content.source in ['shopify', 'custom']:
|
||||
content = self.optimizer_service.optimize_from_external_sync(content.id)
|
||||
else:
|
||||
content = self.optimizer_service.optimize_manual(content.id)
|
||||
|
||||
logger.info(f"Optimized synced content {content_id}")
|
||||
except Exception as e:
|
||||
logger.error(f"Error in optimization stage for content {content_id}: {str(e)}", exc_info=True)
|
||||
raise
|
||||
|
||||
return content
|
||||
|
||||
def batch_process_writer_content(
|
||||
self,
|
||||
content_ids: List[int],
|
||||
stages: Optional[List[str]] = None
|
||||
) -> List[Content]:
|
||||
"""
|
||||
Batch process multiple Writer content items.
|
||||
|
||||
Args:
|
||||
content_ids: List of content IDs
|
||||
stages: List of stages to run
|
||||
|
||||
Returns:
|
||||
List of processed Content instances
|
||||
"""
|
||||
results = []
|
||||
for content_id in content_ids:
|
||||
try:
|
||||
result = self.process_writer_content(content_id, stages)
|
||||
results.append(result)
|
||||
except Exception as e:
|
||||
logger.error(f"Error processing content {content_id}: {str(e)}", exc_info=True)
|
||||
# Continue with other items
|
||||
continue
|
||||
|
||||
return results
|
||||
|
||||
|
||||
Reference in New Issue
Block a user