""" Logging admin interfaces (TaskExecutionLog, AccessLog) """ from django.contrib import admin from django.utils.safestring import mark_safe from django.utils.html import format_html from django.shortcuts import redirect from django.contrib import messages from django.utils.timezone import localtime from vpn.models import TaskExecutionLog, AccessLog from .base import BaseVPNAdmin from vpn.utils import format_object @admin.register(TaskExecutionLog) class TaskExecutionLogAdmin(BaseVPNAdmin): list_display = ('task_name_display', 'action', 'status_display', 'server', 'user', 'execution_time_display', 'created_at') list_filter = ('task_name', 'status', 'server', 'created_at') search_fields = ('task_id', 'task_name', 'action', 'user__username', 'server__name', 'message') readonly_fields = ('task_id', 'task_name', 'server', 'user', 'action', 'status', 'message_formatted', 'execution_time', 'created_at') ordering = ('-created_at',) list_per_page = 100 date_hierarchy = 'created_at' actions = ['trigger_full_sync', 'trigger_statistics_update'] fieldsets = ( ('Task Information', { 'fields': ('task_id', 'task_name', 'action', 'status') }), ('Related Objects', { 'fields': ('server', 'user') }), ('Execution Details', { 'fields': ('message_formatted', 'execution_time', 'created_at') }), ) def trigger_full_sync(self, request, queryset): """Trigger manual full synchronization of all servers""" # This action doesn't require selected items try: from vpn.tasks import sync_all_users # Start the sync task task = sync_all_users.delay() self.message_user( request, f'Full synchronization started successfully. Task ID: {task.id}. Check logs below for progress.', level=messages.SUCCESS ) except Exception as e: self.message_user( request, f'Failed to start full synchronization: {e}', level=messages.ERROR ) trigger_full_sync.short_description = "๐ Trigger full sync of all servers" def trigger_statistics_update(self, request, queryset): """Trigger manual update of user statistics cache""" # This action doesn't require selected items try: from vpn.tasks import update_user_statistics # Start the statistics update task task = update_user_statistics.delay() self.message_user( request, f'User statistics update started successfully. Task ID: {task.id}. Check logs below for progress.', level=messages.SUCCESS ) except Exception as e: self.message_user( request, f'Failed to start statistics update: {e}', level=messages.ERROR ) trigger_statistics_update.short_description = "๐ Update user statistics cache" def get_actions(self, request): """Remove default delete action for logs""" actions = super().get_actions(request) if 'delete_selected' in actions: del actions['delete_selected'] return actions @admin.display(description='Task', ordering='task_name') def task_name_display(self, obj): task_names = { 'sync_all_servers': '๐ Sync All', 'sync_server_users': '๐ฅ Server Sync', 'sync_server_info': 'โ๏ธ Server Info', 'sync_user_on_server': '๐ค User Sync', 'cleanup_task_logs': '๐งน Cleanup', 'update_user_statistics': '๐ Statistics', } return task_names.get(obj.task_name, obj.task_name) @admin.display(description='Status', ordering='status') def status_display(self, obj): status_icons = { 'STARTED': '๐ก Started', 'SUCCESS': 'โ Success', 'FAILURE': 'โ Failed', 'RETRY': '๐ Retry', } return status_icons.get(obj.status, obj.status) @admin.display(description='Time', ordering='execution_time') def execution_time_display(self, obj): if obj.execution_time: if obj.execution_time < 1: return f"{obj.execution_time*1000:.0f}ms" else: return f"{obj.execution_time:.2f}s" return '-' @admin.display(description='Message') def message_formatted(self, obj): if obj.message: return mark_safe(f"
{obj.message}")
return '-'
def has_add_permission(self, request):
return False
def has_change_permission(self, request, obj=None):
return False
def changelist_view(self, request, extra_context=None):
"""Override to handle actions that don't require item selection"""
# Handle actions that don't require selection
if 'action' in request.POST:
action = request.POST['action']
if action == 'trigger_full_sync':
# Call the action directly without queryset requirement
self.trigger_full_sync(request, None)
# Return redirect to prevent AttributeError
return redirect(request.get_full_path())
elif action == 'trigger_statistics_update':
# Call the statistics update action
self.trigger_statistics_update(request, None)
# Return redirect to prevent AttributeError
return redirect(request.get_full_path())
return super().changelist_view(request, extra_context)
@admin.register(AccessLog)
class AccessLogAdmin(BaseVPNAdmin):
list_display = ('user', 'server', 'acl_link_display', 'action', 'formatted_timestamp')
list_filter = ('user', 'server', 'action', 'timestamp')
search_fields = ('user', 'server', 'acl_link_id', 'action', 'timestamp', 'data')
readonly_fields = ('server', 'user', 'acl_link_id', 'formatted_timestamp', 'action', 'formated_data')
@admin.display(description='Link', ordering='acl_link_id')
def acl_link_display(self, obj):
if obj.acl_link_id:
return format_html(
'{}',
obj.acl_link_id[:12] + '...' if len(obj.acl_link_id) > 12 else obj.acl_link_id
)
return '-'
@admin.display(description='Timestamp')
def formatted_timestamp(self, obj):
local_time = localtime(obj.timestamp)
return local_time.strftime('%Y-%m-%d %H:%M:%S %Z')
@admin.display(description='Details')
def formated_data(self, obj):
return format_object(obj.data)