""" Observations admin configuration. """ from django.contrib import admin from django.utils.html import format_html from .models import ( Observation, ObservationAttachment, ObservationCategory, ObservationNote, ObservationStatusLog, ) @admin.register(ObservationCategory) class ObservationCategoryAdmin(admin.ModelAdmin): """Admin for ObservationCategory model.""" list_display = ['name_en', 'name_ar', 'is_active', 'sort_order', 'observation_count', 'created_at'] list_filter = ['is_active', 'created_at'] search_fields = ['name_en', 'name_ar', 'description'] ordering = ['sort_order', 'name_en'] fieldsets = ( (None, { 'fields': ('name_en', 'name_ar', 'description') }), ('Settings', { 'fields': ('icon', 'sort_order', 'is_active') }), ) def observation_count(self, obj): return obj.observations.count() observation_count.short_description = 'Observations' class ObservationAttachmentInline(admin.TabularInline): """Inline admin for ObservationAttachment.""" model = ObservationAttachment extra = 0 readonly_fields = ['filename', 'file_type', 'file_size', 'created_at'] fields = ['file', 'filename', 'file_type', 'file_size', 'description', 'created_at'] class ObservationNoteInline(admin.TabularInline): """Inline admin for ObservationNote.""" model = ObservationNote extra = 0 readonly_fields = ['created_by', 'created_at'] fields = ['note', 'is_internal', 'created_by', 'created_at'] class ObservationStatusLogInline(admin.TabularInline): """Inline admin for ObservationStatusLog.""" model = ObservationStatusLog extra = 0 readonly_fields = ['from_status', 'to_status', 'changed_by', 'comment', 'created_at'] fields = ['from_status', 'to_status', 'changed_by', 'comment', 'created_at'] can_delete = False def has_add_permission(self, request, obj=None): return False @admin.register(Observation) class ObservationAdmin(admin.ModelAdmin): """Admin for Observation model.""" list_display = [ 'tracking_code', 'title_display', 'category', 'severity_badge', 'status_badge', 'reporter_display', 'assigned_department', 'assigned_to', 'created_at' ] list_filter = [ 'status', 'severity', 'category', 'assigned_department', 'created_at', 'triaged_at', 'resolved_at' ] search_fields = [ 'tracking_code', 'title', 'description', 'reporter_name', 'reporter_staff_id', 'location_text' ] readonly_fields = [ 'tracking_code', 'created_at', 'updated_at', 'triaged_at', 'resolved_at', 'closed_at', 'client_ip', 'user_agent' ] ordering = ['-created_at'] date_hierarchy = 'created_at' fieldsets = ( ('Tracking', { 'fields': ('tracking_code', 'status') }), ('Content', { 'fields': ('category', 'title', 'description', 'severity') }), ('Location & Time', { 'fields': ('location_text', 'incident_datetime') }), ('Reporter Information', { 'fields': ('reporter_staff_id', 'reporter_name', 'reporter_phone', 'reporter_email'), 'classes': ('collapse',) }), ('Assignment', { 'fields': ('assigned_department', 'assigned_to') }), ('Triage', { 'fields': ('triaged_by', 'triaged_at'), 'classes': ('collapse',) }), ('Resolution', { 'fields': ('resolved_by', 'resolved_at', 'resolution_notes'), 'classes': ('collapse',) }), ('Closure', { 'fields': ('closed_by', 'closed_at'), 'classes': ('collapse',) }), ('Action Center', { 'fields': ('action_id',), 'classes': ('collapse',) }), ('Metadata', { 'fields': ('client_ip', 'user_agent', 'metadata', 'created_at', 'updated_at'), 'classes': ('collapse',) }), ) inlines = [ObservationAttachmentInline, ObservationNoteInline, ObservationStatusLogInline] def title_display(self, obj): if obj.title: return obj.title[:50] + '...' if len(obj.title) > 50 else obj.title return obj.description[:50] + '...' if len(obj.description) > 50 else obj.description title_display.short_description = 'Title/Description' def severity_badge(self, obj): colors = { 'low': '#28a745', 'medium': '#ffc107', 'high': '#dc3545', 'critical': '#343a40', } color = colors.get(obj.severity, '#6c757d') return format_html( '{}', color, obj.get_severity_display() ) severity_badge.short_description = 'Severity' def status_badge(self, obj): colors = { 'new': '#007bff', 'triaged': '#17a2b8', 'assigned': '#17a2b8', 'in_progress': '#ffc107', 'resolved': '#28a745', 'closed': '#6c757d', 'rejected': '#dc3545', 'duplicate': '#6c757d', } color = colors.get(obj.status, '#6c757d') return format_html( '{}', color, obj.get_status_display() ) status_badge.short_description = 'Status' def reporter_display(self, obj): if obj.is_anonymous: return format_html('Anonymous') return obj.reporter_display reporter_display.short_description = 'Reporter' @admin.register(ObservationAttachment) class ObservationAttachmentAdmin(admin.ModelAdmin): """Admin for ObservationAttachment model.""" list_display = ['observation', 'filename', 'file_type', 'file_size_display', 'created_at'] list_filter = ['file_type', 'created_at'] search_fields = ['observation__tracking_code', 'filename', 'description'] readonly_fields = ['file_size', 'created_at'] def file_size_display(self, obj): if obj.file_size < 1024: return f"{obj.file_size} B" elif obj.file_size < 1024 * 1024: return f"{obj.file_size / 1024:.1f} KB" else: return f"{obj.file_size / (1024 * 1024):.1f} MB" file_size_display.short_description = 'Size' @admin.register(ObservationNote) class ObservationNoteAdmin(admin.ModelAdmin): """Admin for ObservationNote model.""" list_display = ['observation', 'note_preview', 'created_by', 'is_internal', 'created_at'] list_filter = ['is_internal', 'created_at'] search_fields = ['observation__tracking_code', 'note', 'created_by__email'] readonly_fields = ['created_at'] def note_preview(self, obj): return obj.note[:100] + '...' if len(obj.note) > 100 else obj.note note_preview.short_description = 'Note' @admin.register(ObservationStatusLog) class ObservationStatusLogAdmin(admin.ModelAdmin): """Admin for ObservationStatusLog model.""" list_display = ['observation', 'from_status', 'to_status', 'changed_by', 'created_at'] list_filter = ['from_status', 'to_status', 'created_at'] search_fields = ['observation__tracking_code', 'comment', 'changed_by__email'] readonly_fields = ['observation', 'from_status', 'to_status', 'changed_by', 'comment', 'created_at'] def has_add_permission(self, request): return False def has_change_permission(self, request, obj=None): return False