from django.contrib import admin
from django.utils.html import format_html
from django.utils.translation import gettext_lazy as _
from django.db.models import Count
from unfold.admin import ModelAdmin, TabularInline
from unfold.contrib.filters.admin import RangeNumericFilter, RangeDateTimeFilter
from apps.chat.models import RoomMessage, ChatMessage, MessageReadStatus
from utils.admin import project_admin_site
class ChatMessageInline(TabularInline):
model = ChatMessage
extra = 0
fields = ('sender', 'content', 'content_type', 'sent_at', 'is_deleted')
readonly_fields = ('sent_at',)
can_delete = False
show_change_link = True
classes = ['collapse']
verbose_name = _("Message")
verbose_name_plural = _("Messages")
class MessageReadStatusAdmin(ModelAdmin):
list_display = (
'user', 'message', 'is_read_status', 'read_at',
)
list_filter = (
('read_at', RangeDateTimeFilter),
'is_read',
)
search_fields = ('user__username', 'user__email', 'message__content')
readonly_fields = ('read_at',)
def is_read_status(self, obj):
if obj.is_read:
return format_html('Read')
return format_html('Unread')
is_read_status.short_description = _("Read Status")
class RoomMessageAdmin(ModelAdmin):
list_display = (
'name', 'room_type_badge', 'course', 'initiator',
'messages_count', 'view_messages_button'
)
list_filter = (
'room_type',
('created_at', RangeDateTimeFilter),
('updated_at', RangeDateTimeFilter),
'course'
)
search_fields = ('name', 'description', 'course__title', 'initiator__username', 'recipient__username')
ordering = ('-created_at',)
readonly_fields = ('created_at', 'updated_at', 'messages_count')
inlines = [ChatMessageInline]
fieldsets = (
(_("Room Information"), {
'fields': ('name', 'description', 'room_type', 'messages_count'),
'classes': ('grid-col-2',),
}),
(_("Relations"), {
'fields': ('course', 'initiator', 'recipient'),
'classes': ('grid-col-2',),
}),
(_("Timestamps"), {
'fields': ('created_at', 'updated_at'),
'classes': ('grid-col-2',),
}),
)
def messages_count(self, obj):
count = obj.messages.count()
return format_html('{}', count)
messages_count.short_description = _("Messages Count")
def room_type_badge(self, obj):
if obj.room_type == 'group':
return format_html('Group')
return format_html('Private')
room_type_badge.short_description = _("Room Type")
def get_queryset(self, request):
queryset = super().get_queryset(request)
queryset = queryset.annotate(
total_messages=Count('messages')
)
return queryset
def view_messages_button(self, obj):
from django.urls import reverse
url = f"{reverse('admin:chat_chatmessage_changelist')}?room__id__exact={obj.id}"
return format_html(
''
'chat {}',
url, _("View Messages")
)
view_messages_button.short_description = _("Messages")
class MessageReadStatusInline(TabularInline):
model = MessageReadStatus
extra = 0
fields = ('user', 'is_read', 'read_at')
readonly_fields = ('read_at',)
can_delete = False
show_change_link = True
classes = ['collapse']
verbose_name = _("Read Status")
verbose_name_plural = _("Read Statuses")
class ChatMessageAdmin(ModelAdmin):
change_list_template = 'admin/chat/chatmessage/change_list.html'
list_display = (
'id', 'room', 'sender', 'content_type_badge', 'content_preview',
'content_size_display', 'sent_at', 'is_deleted_status'
)
list_filter = (
'room',
'content_type',
'is_deleted',
('sent_at', RangeDateTimeFilter),
('updated_at', RangeDateTimeFilter),
('content_size', RangeNumericFilter)
)
search_fields = ('room__name', 'sender__username', 'content')
ordering = ('-sent_at',)
readonly_fields = ('sent_at', 'updated_at', 'content_size')
inlines = [MessageReadStatusInline]
fieldsets = (
(_("Message Information"), {
'fields': ('room', 'sender', 'content', 'content_type'),
'classes': ('grid-col-2',),
}),
(_("Additional Info"), {
'fields': ('content_size',),
'classes': ('grid-col-1',),
}),
(_("Status"), {
'fields': ('is_deleted', 'deleted_at'),
'classes': ('grid-col-2',),
}),
(_("Timestamps"), {
'fields': ('sent_at', 'updated_at'),
'classes': ('grid-col-2',),
}),
)
def content_preview(self, obj):
if obj.content_type == 'text':
preview = obj.content[:50] + '...' if len(obj.content) > 50 else obj.content
return preview
return f"{obj.content_type} content"
content_preview.short_description = _("Content Preview")
def content_type_badge(self, obj):
badges = {
'text': ('blue', 'Text'),
'file': ('yellow', 'File'),
'audio': ('green', 'Audio'),
'image': ('pink', 'Image'),
}
color, label = badges.get(obj.content_type, ('gray', obj.content_type))
return format_html(
'{}',
color, color, label
)
content_type_badge.short_description = _("Type")
def is_deleted_status(self, obj):
if obj.is_deleted:
return format_html('Deleted')
return format_html('Active')
is_deleted_status.short_description = _("Status")
def content_size_display(self, obj):
if obj.content_size:
# Format size in KB if larger than 1024 bytes
if obj.content_size > 1024:
size_kb = obj.content_size / 1024
return f"{size_kb:.1f} KB"
return f"{obj.content_size} bytes"
return "-"
content_size_display.short_description = _("Size")
# Register models with the custom admin site
project_admin_site.register(RoomMessage, RoomMessageAdmin)
project_admin_site.register(ChatMessage, ChatMessageAdmin)
project_admin_site.register(MessageReadStatus, MessageReadStatusAdmin)