""" Django admin for moderation models. """ from django.contrib import admin from django.utils.html import format_html from django.urls import reverse from django.utils import timezone from unfold.admin import ModelAdmin from unfold.decorators import display from apps.moderation.models import ContentSubmission, SubmissionItem, ModerationLock @admin.register(ContentSubmission) class ContentSubmissionAdmin(ModelAdmin): """Admin for ContentSubmission model.""" list_display = [ 'title_with_icon', 'status_badge', 'entity_info', 'user', 'items_summary', 'locked_info', 'created', ] list_filter = [ 'status', 'submission_type', 'entity_type', 'created', ] search_fields = [ 'title', 'description', 'user__email', 'user__username', ] readonly_fields = [ 'id', 'status', 'entity_type', 'entity_id', 'locked_by', 'locked_at', 'reviewed_by', 'reviewed_at', 'created', 'modified', ] fieldsets = ( ('Submission Info', { 'fields': ( 'id', 'title', 'description', 'submission_type', 'status', ) }), ('Entity', { 'fields': ( 'entity_type', 'entity_id', ) }), ('User Info', { 'fields': ( 'user', 'source', 'ip_address', 'user_agent', ) }), ('Review Info', { 'fields': ( 'locked_by', 'locked_at', 'reviewed_by', 'reviewed_at', 'rejection_reason', ) }), ('Metadata', { 'fields': ( 'metadata', 'created', 'modified', ), 'classes': ('collapse',) }), ) @display(description='Title', ordering='title') def title_with_icon(self, obj): """Display title with submission type icon.""" icons = { 'create': '➕', 'update': '✏️', 'delete': '🗑️', } icon = icons.get(obj.submission_type, '📝') return f"{icon} {obj.title}" @display(description='Status', ordering='status') def status_badge(self, obj): """Display colored status badge.""" colors = { 'draft': 'gray', 'pending': 'blue', 'reviewing': 'orange', 'approved': 'green', 'rejected': 'red', } color = colors.get(obj.status, 'gray') return format_html( '{}', color, obj.get_status_display() ) @display(description='Entity') def entity_info(self, obj): """Display entity type and ID.""" return f"{obj.entity_type.model} #{str(obj.entity_id)[:8]}" @display(description='Items') def items_summary(self, obj): """Display item counts.""" total = obj.get_items_count() approved = obj.get_approved_items_count() rejected = obj.get_rejected_items_count() pending = total - approved - rejected return format_html( '{} / ' '{} / ' '{}', pending, approved, rejected ) @display(description='Lock Status') def locked_info(self, obj): """Display lock information.""" if obj.locked_by: is_expired = not obj.is_locked() status = '🔓 Expired' if is_expired else '🔒 Locked' return f"{status} by {obj.locked_by.email}" return '✅ Unlocked' def get_queryset(self, request): """Optimize queryset with select_related.""" qs = super().get_queryset(request) return qs.select_related( 'user', 'entity_type', 'locked_by', 'reviewed_by' ).prefetch_related('items') class SubmissionItemInline(admin.TabularInline): """Inline admin for submission items.""" model = SubmissionItem extra = 0 fields = [ 'field_label', 'old_value_display', 'new_value_display', 'change_type', 'status', 'reviewed_by', ] readonly_fields = [ 'field_label', 'old_value_display', 'new_value_display', 'change_type', 'status', 'reviewed_by', ] can_delete = False def has_add_permission(self, request, obj=None): return False @admin.register(SubmissionItem) class SubmissionItemAdmin(ModelAdmin): """Admin for SubmissionItem model.""" list_display = [ 'field_label', 'submission_title', 'change_type_badge', 'status_badge', 'old_value_display', 'new_value_display', 'reviewed_by', ] list_filter = [ 'status', 'change_type', 'is_required', 'created', ] search_fields = [ 'field_name', 'field_label', 'submission__title', ] readonly_fields = [ 'id', 'submission', 'field_name', 'field_label', 'old_value', 'new_value', 'old_value_display', 'new_value_display', 'status', 'reviewed_by', 'reviewed_at', 'created', 'modified', ] fieldsets = ( ('Item Info', { 'fields': ( 'id', 'submission', 'field_name', 'field_label', 'change_type', 'is_required', 'order', ) }), ('Values', { 'fields': ( 'old_value', 'new_value', 'old_value_display', 'new_value_display', ) }), ('Review Info', { 'fields': ( 'status', 'reviewed_by', 'reviewed_at', 'rejection_reason', ) }), ('Timestamps', { 'fields': ( 'created', 'modified', ) }), ) @display(description='Submission') def submission_title(self, obj): """Display submission title with link.""" url = reverse('admin:moderation_contentsubmission_change', args=[obj.submission.id]) return format_html('{}', url, obj.submission.title) @display(description='Type', ordering='change_type') def change_type_badge(self, obj): """Display colored change type badge.""" colors = { 'add': 'green', 'modify': 'blue', 'remove': 'red', } color = colors.get(obj.change_type, 'gray') return format_html( '{}', color, obj.get_change_type_display() ) @display(description='Status', ordering='status') def status_badge(self, obj): """Display colored status badge.""" colors = { 'pending': 'orange', 'approved': 'green', 'rejected': 'red', } color = colors.get(obj.status, 'gray') return format_html( '{}', color, obj.get_status_display() ) def get_queryset(self, request): """Optimize queryset with select_related.""" qs = super().get_queryset(request) return qs.select_related('submission', 'reviewed_by') @admin.register(ModerationLock) class ModerationLockAdmin(ModelAdmin): """Admin for ModerationLock model.""" list_display = [ 'submission_title', 'locked_by', 'locked_at', 'expires_at', 'status_indicator', 'lock_duration', ] list_filter = [ 'is_active', 'locked_at', 'expires_at', ] search_fields = [ 'submission__title', 'locked_by__email', 'locked_by__username', ] readonly_fields = [ 'id', 'submission', 'locked_by', 'locked_at', 'expires_at', 'is_active', 'released_at', 'lock_duration', 'is_expired_display', 'created', 'modified', ] fieldsets = ( ('Lock Info', { 'fields': ( 'id', 'submission', 'locked_by', 'is_active', ) }), ('Timing', { 'fields': ( 'locked_at', 'expires_at', 'released_at', 'lock_duration', 'is_expired_display', ) }), ('Timestamps', { 'fields': ( 'created', 'modified', ) }), ) @display(description='Submission') def submission_title(self, obj): """Display submission title with link.""" url = reverse('admin:moderation_contentsubmission_change', args=[obj.submission.id]) return format_html('{}', url, obj.submission.title) @display(description='Status') def status_indicator(self, obj): """Display lock status.""" if not obj.is_active: return format_html( '🔓 Released' ) elif obj.is_expired(): return format_html( '⏰ Expired' ) else: return format_html( '🔒 Active' ) @display(description='Duration') def lock_duration(self, obj): """Display lock duration.""" if obj.released_at: duration = obj.released_at - obj.locked_at else: duration = timezone.now() - obj.locked_at minutes = int(duration.total_seconds() / 60) return f"{minutes} minutes" @display(description='Expired?') def is_expired_display(self, obj): """Display if lock is expired.""" if not obj.is_active: return 'N/A (Released)' return 'Yes' if obj.is_expired() else 'No' def get_queryset(self, request): """Optimize queryset with select_related.""" qs = super().get_queryset(request) return qs.select_related('submission', 'locked_by')