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:
IGNY8 VPS (Salman)
2025-11-17 11:15:15 +00:00
parent fe95d09bbe
commit 9930728e8a
19 changed files with 2281 additions and 1 deletions

View File

@@ -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):

View File

@@ -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']

View File

@@ -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