""" Cascade signals for Planning models Handles status updates and relationship cleanup when parent records are deleted """ import logging from django.db.models.signals import pre_delete, post_save from django.dispatch import receiver logger = logging.getLogger(__name__) @receiver(pre_delete, sender='planner.Clusters') def handle_cluster_soft_delete(sender, instance, **kwargs): """ When a Cluster is deleted: - Set Keywords.cluster = NULL - Reset Keywords.status to 'new' - Set ContentIdeas.keyword_cluster = NULL - Reset ContentIdeas.status to 'new' """ from igny8_core.business.planning.models import Keywords, ContentIdeas # Check if this is a soft delete (is_deleted=True) vs hard delete # Soft deletes trigger delete() which calls soft_delete() if hasattr(instance, 'is_deleted') and instance.is_deleted: return # Skip if already soft-deleted try: # Update related Keywords - clear cluster FK and reset status updated_keywords = Keywords.objects.filter(cluster=instance).update( cluster=None, status='new' ) if updated_keywords: logger.info( f"[Cascade] Cluster '{instance.name}' (ID: {instance.id}) deleted: " f"Reset {updated_keywords} keywords to status='new', cluster=NULL" ) # Update related ContentIdeas - clear cluster FK and reset status updated_ideas = ContentIdeas.objects.filter(keyword_cluster=instance).update( keyword_cluster=None, status='new' ) if updated_ideas: logger.info( f"[Cascade] Cluster '{instance.name}' (ID: {instance.id}) deleted: " f"Reset {updated_ideas} content ideas to status='new', keyword_cluster=NULL" ) except Exception as e: logger.error(f"[Cascade] Error handling cluster deletion cascade: {e}", exc_info=True) @receiver(pre_delete, sender='planner.ContentIdeas') def handle_idea_soft_delete(sender, instance, **kwargs): """ When a ContentIdea is deleted: - Set Tasks.idea = NULL (don't delete tasks, they may have content) - Log orphaned tasks """ from igny8_core.business.content.models import Tasks if hasattr(instance, 'is_deleted') and instance.is_deleted: return try: # Update related Tasks - clear idea FK updated_tasks = Tasks.objects.filter(idea=instance).update(idea=None) if updated_tasks: logger.info( f"[Cascade] ContentIdea '{instance.idea_title}' (ID: {instance.id}) deleted: " f"Cleared idea reference from {updated_tasks} tasks" ) except Exception as e: logger.error(f"[Cascade] Error handling content idea deletion cascade: {e}", exc_info=True) @receiver(pre_delete, sender='writer.Tasks') def handle_task_soft_delete(sender, instance, **kwargs): """ When a Task is deleted: - Set Content.task = NULL """ from igny8_core.business.content.models import Content if hasattr(instance, 'is_deleted') and instance.is_deleted: return try: # Update related Content - clear task FK updated_content = Content.objects.filter(task=instance).update(task=None) if updated_content: logger.info( f"[Cascade] Task '{instance.title}' (ID: {instance.id}) deleted: " f"Cleared task reference from {updated_content} content items" ) except Exception as e: logger.error(f"[Cascade] Error handling task deletion cascade: {e}", exc_info=True) @receiver(pre_delete, sender='writer.Content') def handle_content_soft_delete(sender, instance, **kwargs): """ When Content is deleted: - Soft delete related Images (cascade soft delete) - Clear PublishingRecord references """ from igny8_core.business.content.models import Images if hasattr(instance, 'is_deleted') and instance.is_deleted: return try: # Soft delete related Images related_images = Images.objects.filter(content=instance) for image in related_images: image.soft_delete(reason='cascade_from_content') count = related_images.count() if count: logger.info( f"[Cascade] Content '{instance.title}' (ID: {instance.id}) deleted: " f"Soft deleted {count} related images" ) except Exception as e: logger.error(f"[Cascade] Error handling content deletion cascade: {e}", exc_info=True)