""" Signal handlers for moderation-related FSM state transitions. This module provides signal handlers that execute when moderation models (EditSubmission, PhotoSubmission, ModerationReport, etc.) undergo state transitions. """ import logging from django.conf import settings from django.dispatch import receiver from apps.core.state_machine.signals import ( post_state_transition, state_transition_failed, ) logger = logging.getLogger(__name__) def handle_submission_approved(instance, source, target, user, context=None, **kwargs): """ Handle submission approval transitions. Called when an EditSubmission or PhotoSubmission is approved. Args: instance: The submission instance. source: The source state. target: The target state. user: The user who approved. context: Optional TransitionContext. """ if target != 'APPROVED': return logger.info( f"Submission {instance.pk} approved by {user if user else 'system'}" ) # Trigger notification (handled by NotificationCallback) # Invalidate cache (handled by CacheInvalidationCallback) # Apply the submission changes if applicable if hasattr(instance, 'apply_changes'): try: instance.apply_changes() logger.info(f"Applied changes for submission {instance.pk}") except Exception as e: logger.exception( f"Failed to apply changes for submission {instance.pk}: {e}" ) def handle_submission_rejected(instance, source, target, user, context=None, **kwargs): """ Handle submission rejection transitions. Called when an EditSubmission or PhotoSubmission is rejected. Args: instance: The submission instance. source: The source state. target: The target state. user: The user who rejected. context: Optional TransitionContext. """ if target != 'REJECTED': return reason = context.extra_data.get('reason', '') if context else '' logger.info( f"Submission {instance.pk} rejected by {user if user else 'system'}" f"{f': {reason}' if reason else ''}" ) def handle_submission_escalated(instance, source, target, user, context=None, **kwargs): """ Handle submission escalation transitions. Called when an EditSubmission or PhotoSubmission is escalated. Args: instance: The submission instance. source: The source state. target: The target state. user: The user who escalated. context: Optional TransitionContext. """ if target != 'ESCALATED': return reason = context.extra_data.get('reason', '') if context else '' logger.info( f"Submission {instance.pk} escalated by {user if user else 'system'}" f"{f': {reason}' if reason else ''}" ) # Create escalation task if task system is available _create_escalation_task(instance, user, reason) def handle_report_resolved(instance, source, target, user, context=None, **kwargs): """ Handle moderation report resolution. Called when a ModerationReport is resolved. Args: instance: The ModerationReport instance. source: The source state. target: The target state. user: The user who resolved. context: Optional TransitionContext. """ if target != 'RESOLVED': return logger.info( f"ModerationReport {instance.pk} resolved by {user if user else 'system'}" ) # Update related queue items _update_related_queue_items(instance, 'COMPLETED') def handle_queue_completed(instance, source, target, user, context=None, **kwargs): """ Handle moderation queue completion. Called when a ModerationQueue item is completed. Args: instance: The ModerationQueue instance. source: The source state. target: The target state. user: The user who completed. context: Optional TransitionContext. """ if target != 'COMPLETED': return logger.info( f"ModerationQueue {instance.pk} completed by {user if user else 'system'}" ) # Update moderation statistics _update_moderation_stats(instance, user) def handle_bulk_operation_status(instance, source, target, user, context=None, **kwargs): """ Handle bulk operation status changes. Called when a BulkOperation transitions between states. Args: instance: The BulkOperation instance. source: The source state. target: The target state. user: The user who initiated the change. context: Optional TransitionContext. """ logger.info( f"BulkOperation {instance.pk} transitioned: {source} → {target}" ) if target == 'COMPLETED': _finalize_bulk_operation(instance, success=True) elif target == 'FAILED': _finalize_bulk_operation(instance, success=False) # Helper functions def _create_escalation_task(instance, user, reason): """Create an escalation task for admin review.""" try: from apps.moderation.models import ModerationQueue # Create a queue item for the escalated submission ModerationQueue.objects.create( content_object=instance, priority='HIGH', reason=f"Escalated: {reason}" if reason else "Escalated for review", created_by=user, ) logger.info(f"Created escalation queue item for submission {instance.pk}") except ImportError: logger.debug("ModerationQueue model not available") except Exception as e: logger.warning(f"Failed to create escalation task: {e}") def _update_related_queue_items(instance, status): """Update queue items related to a moderation object.""" try: from django.contrib.contenttypes.models import ContentType from apps.moderation.models import ModerationQueue content_type = ContentType.objects.get_for_model(type(instance)) queue_items = ModerationQueue.objects.filter( content_type=content_type, object_id=instance.pk, ).exclude(status=status) updated = queue_items.update(status=status) if updated: logger.info(f"Updated {updated} queue items to {status}") except ImportError: logger.debug("ModerationQueue model not available") except Exception as e: logger.warning(f"Failed to update queue items: {e}") def _update_moderation_stats(instance, user): """Update moderation statistics for a user.""" if not user: return try: # Update user's moderation count if they have a profile profile = getattr(user, 'profile', None) if profile and hasattr(profile, 'moderation_count'): profile.moderation_count += 1 profile.save(update_fields=['moderation_count']) logger.debug(f"Updated moderation count for {user}") except Exception as e: logger.warning(f"Failed to update moderation stats: {e}") def _finalize_bulk_operation(instance, success): """Finalize a bulk operation after completion or failure.""" try: from django.utils import timezone instance.completed_at = timezone.now() instance.save(update_fields=['completed_at']) if success: logger.info( f"BulkOperation {instance.pk} completed successfully: " f"{getattr(instance, 'success_count', 0)} succeeded, " f"{getattr(instance, 'failure_count', 0)} failed" ) else: logger.warning( f"BulkOperation {instance.pk} failed: " f"{getattr(instance, 'error_message', 'Unknown error')}" ) except Exception as e: logger.warning(f"Failed to finalize bulk operation: {e}") # Signal handler registration def register_moderation_signal_handlers(): """ Register all moderation signal handlers. This function should be called in the moderation app's AppConfig.ready() method. """ from apps.core.state_machine.signals import register_transition_handler try: from apps.moderation.models import ( EditSubmission, PhotoSubmission, ModerationReport, ModerationQueue, BulkOperation, ) # EditSubmission handlers register_transition_handler( EditSubmission, '*', 'APPROVED', handle_submission_approved, stage='post' ) register_transition_handler( EditSubmission, '*', 'REJECTED', handle_submission_rejected, stage='post' ) register_transition_handler( EditSubmission, '*', 'ESCALATED', handle_submission_escalated, stage='post' ) # PhotoSubmission handlers register_transition_handler( PhotoSubmission, '*', 'APPROVED', handle_submission_approved, stage='post' ) register_transition_handler( PhotoSubmission, '*', 'REJECTED', handle_submission_rejected, stage='post' ) register_transition_handler( PhotoSubmission, '*', 'ESCALATED', handle_submission_escalated, stage='post' ) # ModerationReport handlers register_transition_handler( ModerationReport, '*', 'RESOLVED', handle_report_resolved, stage='post' ) # ModerationQueue handlers register_transition_handler( ModerationQueue, '*', 'COMPLETED', handle_queue_completed, stage='post' ) # BulkOperation handlers register_transition_handler( BulkOperation, '*', '*', handle_bulk_operation_status, stage='post' ) logger.info("Registered moderation signal handlers") except ImportError as e: logger.warning(f"Could not register moderation signal handlers: {e}")