302 lines
9.2 KiB
Python
302 lines
9.2 KiB
Python
"""
|
|
Административная панель для чата.
|
|
"""
|
|
from django.contrib import admin
|
|
from django.utils.html import format_html
|
|
from django.urls import reverse
|
|
from .models import Chat, ChatParticipant, Message, MessageFile, MessageRead, MessageReaction
|
|
|
|
|
|
@admin.register(Chat)
|
|
class ChatAdmin(admin.ModelAdmin):
|
|
"""Админ интерфейс для чатов."""
|
|
|
|
list_display = [
|
|
'uuid_short',
|
|
'name_display',
|
|
'type_badge',
|
|
'created_by_link',
|
|
'participants_count',
|
|
'messages_count',
|
|
'last_message_at',
|
|
'is_archived_badge',
|
|
'created_at'
|
|
]
|
|
|
|
list_filter = [
|
|
'chat_type',
|
|
'is_archived',
|
|
'created_at'
|
|
]
|
|
|
|
search_fields = [
|
|
'uuid',
|
|
'name',
|
|
'description'
|
|
]
|
|
|
|
readonly_fields = [
|
|
'uuid',
|
|
'messages_count',
|
|
'last_message_at',
|
|
'created_at',
|
|
'updated_at'
|
|
]
|
|
|
|
def uuid_short(self, obj):
|
|
"""Короткий UUID."""
|
|
return str(obj.uuid)[:8]
|
|
uuid_short.short_description = 'ID'
|
|
|
|
def name_display(self, obj):
|
|
"""Название чата."""
|
|
return obj.name or f"Чат {str(obj.uuid)[:8]}"
|
|
name_display.short_description = 'Название'
|
|
|
|
def type_badge(self, obj):
|
|
"""Бейдж типа чата."""
|
|
colors = {
|
|
'direct': '#007bff',
|
|
'group': '#28a745'
|
|
}
|
|
return format_html(
|
|
'<span style="background-color: {}; color: white; padding: 3px 10px; border-radius: 3px;">{}</span>',
|
|
colors.get(obj.chat_type, '#000'),
|
|
obj.get_chat_type_display()
|
|
)
|
|
type_badge.short_description = 'Тип'
|
|
|
|
def created_by_link(self, obj):
|
|
"""Ссылка на создателя."""
|
|
if obj.created_by:
|
|
url = reverse('admin:users_user_change', args=[obj.created_by.id])
|
|
return format_html('<a href="{}">{}</a>', url, obj.created_by.get_full_name())
|
|
return '-'
|
|
created_by_link.short_description = 'Создатель'
|
|
|
|
def participants_count(self, obj):
|
|
"""Количество участников."""
|
|
return obj.participants.count()
|
|
participants_count.short_description = 'Участников'
|
|
|
|
def is_archived_badge(self, obj):
|
|
"""Бейдж архивации."""
|
|
if obj.is_archived:
|
|
return format_html('<span style="color: red;">✗ Архивирован</span>')
|
|
return format_html('<span style="color: green;">✓ Активен</span>')
|
|
is_archived_badge.short_description = 'Статус'
|
|
|
|
|
|
@admin.register(ChatParticipant)
|
|
class ChatParticipantAdmin(admin.ModelAdmin):
|
|
"""Админ интерфейс для участников чата."""
|
|
|
|
list_display = [
|
|
'user_link',
|
|
'chat_link',
|
|
'role_badge',
|
|
'unread_count',
|
|
'is_muted',
|
|
'is_pinned',
|
|
'joined_at'
|
|
]
|
|
|
|
list_filter = [
|
|
'role',
|
|
'is_muted',
|
|
'is_pinned',
|
|
'joined_at'
|
|
]
|
|
|
|
search_fields = [
|
|
'user__email',
|
|
'chat__name'
|
|
]
|
|
|
|
def user_link(self, obj):
|
|
"""Ссылка на пользователя."""
|
|
url = reverse('admin:users_user_change', args=[obj.user.id])
|
|
return format_html('<a href="{}">{}</a>', url, obj.user.get_full_name())
|
|
user_link.short_description = 'Пользователь'
|
|
|
|
def chat_link(self, obj):
|
|
"""Ссылка на чат."""
|
|
url = reverse('admin:chat_chat_change', args=[obj.chat.id])
|
|
name = obj.chat.name or f"Чат {str(obj.chat.uuid)[:8]}"
|
|
return format_html('<a href="{}">{}</a>', url, name)
|
|
chat_link.short_description = 'Чат'
|
|
|
|
def role_badge(self, obj):
|
|
"""Бейдж роли."""
|
|
colors = {
|
|
'admin': '#dc3545',
|
|
'member': '#6c757d'
|
|
}
|
|
return format_html(
|
|
'<span style="background-color: {}; color: white; padding: 3px 10px; border-radius: 3px;">{}</span>',
|
|
colors.get(obj.role, '#000'),
|
|
obj.get_role_display()
|
|
)
|
|
role_badge.short_description = 'Роль'
|
|
|
|
|
|
@admin.register(Message)
|
|
class MessageAdmin(admin.ModelAdmin):
|
|
"""Админ интерфейс для сообщений."""
|
|
|
|
list_display = [
|
|
'uuid_short',
|
|
'sender_link',
|
|
'chat_link',
|
|
'content_preview',
|
|
'type_badge',
|
|
'is_edited',
|
|
'is_deleted',
|
|
'created_at'
|
|
]
|
|
|
|
list_filter = [
|
|
'message_type',
|
|
'is_edited',
|
|
'is_deleted',
|
|
'created_at'
|
|
]
|
|
|
|
search_fields = [
|
|
'uuid',
|
|
'content',
|
|
'sender__email'
|
|
]
|
|
|
|
readonly_fields = [
|
|
'uuid',
|
|
'is_edited',
|
|
'edited_at',
|
|
'is_deleted',
|
|
'deleted_at',
|
|
'created_at'
|
|
]
|
|
|
|
def uuid_short(self, obj):
|
|
"""Короткий UUID."""
|
|
return str(obj.uuid)[:8]
|
|
uuid_short.short_description = 'ID'
|
|
|
|
def sender_link(self, obj):
|
|
"""Ссылка на отправителя."""
|
|
if obj.sender:
|
|
url = reverse('admin:users_user_change', args=[obj.sender.id])
|
|
return format_html('<a href="{}">{}</a>', url, obj.sender.get_full_name())
|
|
return 'System'
|
|
sender_link.short_description = 'Отправитель'
|
|
|
|
def chat_link(self, obj):
|
|
"""Ссылка на чат."""
|
|
url = reverse('admin:chat_chat_change', args=[obj.chat.id])
|
|
name = obj.chat.name or f"Чат {str(obj.chat.uuid)[:8]}"
|
|
return format_html('<a href="{}">{}</a>', url, name)
|
|
chat_link.short_description = 'Чат'
|
|
|
|
def content_preview(self, obj):
|
|
"""Превью контента."""
|
|
return obj.content[:100] if len(obj.content) > 100 else obj.content
|
|
content_preview.short_description = 'Содержимое'
|
|
|
|
def type_badge(self, obj):
|
|
"""Бейдж типа."""
|
|
colors = {
|
|
'text': '#007bff',
|
|
'file': '#28a745',
|
|
'image': '#17a2b8',
|
|
'video': '#dc3545',
|
|
'audio': '#ffc107',
|
|
'system': '#6c757d'
|
|
}
|
|
return format_html(
|
|
'<span style="background-color: {}; color: white; padding: 3px 10px; border-radius: 3px;">{}</span>',
|
|
colors.get(obj.message_type, '#000'),
|
|
obj.get_message_type_display()
|
|
)
|
|
type_badge.short_description = 'Тип'
|
|
|
|
|
|
@admin.register(MessageFile)
|
|
class MessageFileAdmin(admin.ModelAdmin):
|
|
"""Админ интерфейс для файлов сообщений."""
|
|
|
|
list_display = [
|
|
'file_name',
|
|
'message_link',
|
|
'file_size_display',
|
|
'file_type',
|
|
'created_at'
|
|
]
|
|
|
|
search_fields = ['file_name', 'file_type']
|
|
|
|
def message_link(self, obj):
|
|
"""Ссылка на сообщение."""
|
|
url = reverse('admin:chat_message_change', args=[obj.message.id])
|
|
return format_html('<a href="{}">{}</a>', url, str(obj.message.uuid)[:8])
|
|
message_link.short_description = 'Сообщение'
|
|
|
|
def file_size_display(self, obj):
|
|
"""Отображение размера."""
|
|
size_mb = obj.file_size / (1024 * 1024)
|
|
if size_mb > 1:
|
|
return f"{size_mb:.2f} МБ"
|
|
size_kb = obj.file_size / 1024
|
|
return f"{size_kb:.2f} КБ"
|
|
file_size_display.short_description = 'Размер'
|
|
|
|
|
|
@admin.register(MessageRead)
|
|
class MessageReadAdmin(admin.ModelAdmin):
|
|
"""Админ интерфейс для прочтений."""
|
|
|
|
list_display = [
|
|
'user_link',
|
|
'message_link',
|
|
'read_at'
|
|
]
|
|
|
|
search_fields = ['user__email']
|
|
|
|
def user_link(self, obj):
|
|
"""Ссылка на пользователя."""
|
|
url = reverse('admin:users_user_change', args=[obj.user.id])
|
|
return format_html('<a href="{}">{}</a>', url, obj.user.get_full_name())
|
|
user_link.short_description = 'Пользователь'
|
|
|
|
def message_link(self, obj):
|
|
"""Ссылка на сообщение."""
|
|
url = reverse('admin:chat_message_change', args=[obj.message.id])
|
|
return format_html('<a href="{}">{}</a>', url, str(obj.message.uuid)[:8])
|
|
message_link.short_description = 'Сообщение'
|
|
|
|
|
|
@admin.register(MessageReaction)
|
|
class MessageReactionAdmin(admin.ModelAdmin):
|
|
"""Админ интерфейс для реакций."""
|
|
|
|
list_display = [
|
|
'user_link',
|
|
'message_link',
|
|
'emoji',
|
|
'created_at'
|
|
]
|
|
|
|
search_fields = ['user__email', 'emoji']
|
|
|
|
def user_link(self, obj):
|
|
"""Ссылка на пользователя."""
|
|
url = reverse('admin:users_user_change', args=[obj.user.id])
|
|
return format_html('<a href="{}">{}</a>', url, obj.user.get_full_name())
|
|
user_link.short_description = 'Пользователь'
|
|
|
|
def message_link(self, obj):
|
|
"""Ссылка на сообщение."""
|
|
url = reverse('admin:chat_message_change', args=[obj.message.id])
|
|
return format_html('<a href="{}">{}</a>', url, str(obj.message.uuid)[:8])
|
|
message_link.short_description = 'Сообщение'
|