Spaces:
Sleeping
Sleeping
File size: 9,760 Bytes
4f01198 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 | """
Django Admin Configuration for PawCare
All sensitive mutations via admin are auto-captured by AuditLogMiddleware.
Caregivers are managed exclusively through this panel.
"""
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.utils.html import format_html
from api.models import (
User, Service, Caregiver, CaregiverService,
Pet, Booking, Conversation, Message,
)
from audit.models import AuditLog
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# User Admin
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
@admin.register(User)
class UserAdmin(BaseUserAdmin):
list_display = ['email', 'full_name', 'role', 'is_active', 'date_joined']
list_filter = ['role', 'is_active', 'is_staff']
search_fields = ['email', 'first_name', 'last_name']
ordering = ['-date_joined']
readonly_fields = ['id', 'date_joined', 'last_login']
fieldsets = (
('Account', {'fields': ('id', 'email', 'password')}),
('Personal Info', {'fields': ('first_name', 'last_name', 'avatar_url')}),
('Encrypted PII', {'fields': ('phone', 'address'), 'classes': ('collapse',)}),
('Role & Permissions', {'fields': ('role', 'is_active', 'is_staff', 'is_superuser', 'groups', 'user_permissions')}),
('Timestamps', {'fields': ('date_joined', 'last_login')}),
)
add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('email', 'first_name', 'last_name', 'role', 'password1', 'password2'),
}),
)
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# Service Admin
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
@admin.register(Service)
class ServiceAdmin(admin.ModelAdmin):
list_display = ['name', 'icon', 'base_price', 'duration_minutes', 'is_active']
list_editable = ['is_active', 'base_price']
search_fields = ['name']
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# Caregiver Admin β primary management interface
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
class CaregiverServiceInline(admin.TabularInline):
model = CaregiverService
extra = 1
fields = ['service', 'price_per_hour', 'is_active']
@admin.register(Caregiver)
class CaregiverAdmin(admin.ModelAdmin):
list_display = [
'user', 'city', 'rating_badge', 'total_bookings',
'is_available', 'is_verified', 'is_featured', 'created_at',
]
list_filter = ['is_available', 'is_verified', 'is_featured', 'background_check_passed', 'city']
search_fields = ['user__email', 'user__first_name', 'user__last_name', 'city']
readonly_fields = ['id', 'rating', 'total_reviews', 'total_bookings', 'created_at', 'updated_at', 'profile_image_preview']
inlines = [CaregiverServiceInline]
list_per_page = 25
actions = ['mark_verified', 'mark_featured', 'mark_unavailable']
fieldsets = (
('Identity', {'fields': ('id', 'user', 'profile_image_preview', 'profile_image_url')}),
('Profile', {'fields': ('bio', 'years_of_experience', 'specializations', 'certifications', 'languages')}),
('Location', {'fields': ('city', 'country', 'latitude', 'longitude')}),
('Media', {'fields': ('gallery_images',), 'classes': ('collapse',)}),
('Stats', {'fields': ('rating', 'total_reviews', 'total_bookings')}),
('Status', {'fields': ('is_available', 'is_verified', 'is_featured', 'background_check_passed')}),
('Sensitive', {'fields': ('emergency_contact',), 'classes': ('collapse',)}),
('Timestamps', {'fields': ('created_at', 'updated_at')}),
)
@admin.display(description='Rating')
def rating_badge(self, obj):
color = '#22c55e' if obj.rating >= 4 else '#f59e0b' if obj.rating >= 3 else '#ef4444'
return format_html(
'<span style="color:{};font-weight:bold">β
{}</span>', color, obj.rating
)
@admin.display(description='Profile Image')
def profile_image_preview(self, obj):
if obj.profile_image_url:
return format_html(
'<img src="{}" style="height:80px;border-radius:8px;"/>', obj.profile_image_url
)
return 'β'
@admin.action(description='β
Mark selected as Verified')
def mark_verified(self, request, queryset):
queryset.update(is_verified=True, background_check_passed=True)
@admin.action(description='β Toggle Featured')
def mark_featured(self, request, queryset):
for c in queryset:
c.is_featured = not c.is_featured
c.save(update_fields=['is_featured'])
@admin.action(description='π΄ Mark as Unavailable')
def mark_unavailable(self, request, queryset):
queryset.update(is_available=False)
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# Pet Admin
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
@admin.register(Pet)
class PetAdmin(admin.ModelAdmin):
list_display = ['name', 'pet_type', 'owner', 'is_vaccinated', 'is_neutered']
list_filter = ['pet_type', 'is_vaccinated', 'is_neutered']
search_fields = ['name', 'owner__email', 'breed']
readonly_fields = ['id', 'created_at', 'updated_at']
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# Booking Admin
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
@admin.register(Booking)
class BookingAdmin(admin.ModelAdmin):
list_display = [
'short_id', 'user', 'caregiver', 'service', 'status',
'scheduled_start', 'price_total', 'rating',
]
list_filter = ['status', 'service', 'payment_status']
search_fields = ['user__email', 'caregiver__user__email']
readonly_fields = ['id', 'price_subtotal', 'price_fees', 'price_total', 'created_at', 'updated_at']
date_hierarchy = 'scheduled_start'
@admin.display(description='ID')
def short_id(self, obj):
return str(obj.id)[:8] + 'β¦'
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# Messaging Admin
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
class MessageInline(admin.TabularInline):
model = Message
extra = 0
readonly_fields = ['sender', 'created_at', 'is_read']
fields = ['sender', 'message_type', 'content', 'is_read', 'created_at']
@admin.register(Conversation)
class ConversationAdmin(admin.ModelAdmin):
list_display = ['id', 'booking', 'created_at', 'updated_at']
readonly_fields = ['id', 'created_at', 'updated_at']
inlines = [MessageInline]
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# Audit Log Admin β read-only
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
@admin.register(AuditLog)
class AuditLogAdmin(admin.ModelAdmin):
list_display = ['timestamp', 'actor_email', 'actor_role', 'action', 'resource_type', 'ip_address', 'response_status']
list_filter = ['action', 'actor_role', 'response_status']
search_fields = ['actor_email', 'resource_id', 'request_path', 'description']
readonly_fields = [f.name for f in AuditLog._meta.fields]
date_hierarchy = 'timestamp'
def has_add_permission(self, request):
return False
def has_change_permission(self, request, obj=None):
return False
def has_delete_permission(self, request, obj=None):
return False
|