""" Admin interface for versioning models. Provides Django admin interface for viewing version history, comparing versions, and managing version records. """ from django.contrib import admin from django.utils.html import format_html from django.urls import reverse from unfold.admin import ModelAdmin from apps.versioning.models import EntityVersion @admin.register(EntityVersion) class EntityVersionAdmin(ModelAdmin): """ Admin interface for EntityVersion model. Provides read-only view of version history with search and filtering. """ # Display settings list_display = [ 'version_number', 'entity_link', 'change_type', 'changed_by_link', 'submission_link', 'changed_field_count', 'created', ] list_filter = [ 'change_type', 'entity_type', 'created', ] search_fields = [ 'entity_id', 'comment', 'changed_by__email', 'changed_by__username', ] ordering = ['-created'] date_hierarchy = 'created' # Read-only admin (versions should not be modified) readonly_fields = [ 'id', 'entity_type', 'entity_id', 'entity_link', 'version_number', 'change_type', 'snapshot_display', 'changed_fields_display', 'changed_by', 'submission', 'comment', 'ip_address', 'user_agent', 'created', 'modified', ] fieldsets = ( ('Version Information', { 'fields': ( 'id', 'version_number', 'change_type', 'created', 'modified', ) }), ('Entity', { 'fields': ( 'entity_type', 'entity_id', 'entity_link', ) }), ('Changes', { 'fields': ( 'changed_fields_display', 'snapshot_display', ) }), ('Metadata', { 'fields': ( 'changed_by', 'submission', 'comment', 'ip_address', 'user_agent', ) }), ) def has_add_permission(self, request): """Disable adding versions manually.""" return False def has_delete_permission(self, request, obj=None): """Disable deleting versions.""" return False def has_change_permission(self, request, obj=None): """Only allow viewing versions, not editing.""" return False def entity_link(self, obj): """Display link to the entity.""" try: entity = obj.entity if entity: # Try to get admin URL for entity admin_url = reverse( f'admin:{obj.entity_type.app_label}_{obj.entity_type.model}_change', args=[entity.pk] ) return format_html( '{}', admin_url, str(entity) ) except: pass return f"{obj.entity_type.model}:{obj.entity_id}" entity_link.short_description = 'Entity' def changed_by_link(self, obj): """Display link to user who made the change.""" if obj.changed_by: try: admin_url = reverse( 'admin:users_user_change', args=[obj.changed_by.pk] ) return format_html( '{}', admin_url, obj.changed_by.email ) except: return obj.changed_by.email return '-' changed_by_link.short_description = 'Changed By' def submission_link(self, obj): """Display link to content submission if applicable.""" if obj.submission: try: admin_url = reverse( 'admin:moderation_contentsubmission_change', args=[obj.submission.pk] ) return format_html( '#{}', admin_url, obj.submission.pk ) except: return str(obj.submission.pk) return '-' submission_link.short_description = 'Submission' def changed_field_count(self, obj): """Display count of changed fields.""" count = len(obj.changed_fields) if count == 0: return '-' return f"{count} field{'s' if count != 1 else ''}" changed_field_count.short_description = 'Changed Fields' def snapshot_display(self, obj): """Display snapshot in a formatted way.""" import json snapshot = obj.get_snapshot_dict() # Format as pretty JSON formatted = json.dumps(snapshot, indent=2, sort_keys=True) return format_html( '
{}',
formatted
)
snapshot_display.short_description = 'Snapshot'
def changed_fields_display(self, obj):
"""Display changed fields in a formatted way."""
if not obj.changed_fields:
return format_html('No fields changed')
html_parts = ['| Field | ') html_parts.append('Old Value | ') html_parts.append('New Value | ') html_parts.append('
|---|---|---|
| {field_name} | ') html_parts.append(f'{old_val} | ') html_parts.append(f'{new_val} | ') html_parts.append('