""" Административная панель для материалов. """ from django.contrib import admin from django.utils.html import format_html from django.urls import reverse from .models import Material, MaterialFolder, MaterialTag, MaterialAccess, StorageQuota @admin.register(Material) class MaterialAdmin(admin.ModelAdmin): """Админ интерфейс для материалов.""" list_display = [ 'title', 'owner_link', 'material_type_badge', 'file_size_display', 'folder_link', 'access_type_badge', 'views_count', 'downloads_count', 'created_at' ] list_filter = [ 'material_type', 'access_type', 'is_featured', 'is_deleted', 'created_at' ] search_fields = [ 'title', 'description', 'file_name', 'owner__email' ] readonly_fields = [ 'file_name', 'file_size', 'file_type', 'views_count', 'downloads_count', 'created_at', 'updated_at', 'deleted_at' ] filter_horizontal = ['tags', 'shared_with'] fieldsets = ( ('Основная информация', { 'fields': ( 'title', 'description', 'owner' ) }), ('Файл', { 'fields': ( 'file', 'file_name', 'file_size', 'file_type', 'url' ) }), ('Организация', { 'fields': ( 'material_type', 'folder', 'tags' ) }), ('Связи', { 'fields': ( 'lesson', 'homework' ) }), ('Доступ', { 'fields': ( 'access_type', 'shared_with', 'allow_download', 'is_featured' ) }), ('Статистика', { 'fields': ( 'views_count', 'downloads_count' ) }), ('Удаление', { 'fields': ( 'is_deleted', 'deleted_at' ) }), ('Временные метки', { 'fields': ( 'created_at', 'updated_at' ) }) ) actions = ['make_public', 'make_private', 'feature_materials'] def owner_link(self, obj): """Ссылка на владельца.""" url = reverse('admin:users_user_change', args=[obj.owner.id]) return format_html('{}', url, obj.owner.get_full_name()) owner_link.short_description = 'Владелец' def folder_link(self, obj): """Ссылка на папку.""" if obj.folder: url = reverse('admin:materials_materialfolder_change', args=[obj.folder.id]) return format_html('{}', url, obj.folder.name) return '-' folder_link.short_description = 'Папка' def material_type_badge(self, obj): """Бейдж типа материала.""" colors = { 'document': '#007bff', 'presentation': '#28a745', 'video': '#dc3545', 'audio': '#ffc107', 'image': '#17a2b8', 'archive': '#6c757d', 'link': '#6610f2', 'other': '#343a40' } return format_html( '{}', colors.get(obj.material_type, '#000'), obj.get_material_type_display() ) material_type_badge.short_description = 'Тип' def access_type_badge(self, obj): """Бейдж типа доступа.""" colors = { 'private': '#6c757d', 'public': '#28a745', 'lesson': '#17a2b8', 'clients': '#ffc107' } return format_html( '{}', colors.get(obj.access_type, '#000'), obj.get_access_type_display() ) access_type_badge.short_description = 'Доступ' def file_size_display(self, obj): """Отображение размера файла.""" if obj.file_size: 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} КБ" return '-' file_size_display.short_description = 'Размер' @admin.action(description='Сделать публичными') def make_public(self, request, queryset): """Сделать материалы публичными.""" queryset.update(access_type='public') @admin.action(description='Сделать приватными') def make_private(self, request, queryset): """Сделать материалы приватными.""" queryset.update(access_type='private') @admin.action(description='Добавить в избранное') def feature_materials(self, request, queryset): """Добавить материалы в избранное.""" queryset.update(is_featured=True) @admin.register(MaterialFolder) class MaterialFolderAdmin(admin.ModelAdmin): """Админ интерфейс для папок.""" list_display = [ 'name', 'owner_link', 'parent', 'path_display', 'is_public', 'materials_count', 'created_at' ] list_filter = [ 'is_public', 'created_at' ] search_fields = [ 'name', 'description', 'owner__email' ] readonly_fields = [ 'materials_count', 'created_at', 'updated_at' ] filter_horizontal = ['shared_with'] def owner_link(self, obj): """Ссылка на владельца.""" url = reverse('admin:users_user_change', args=[obj.owner.id]) return format_html('{}', url, obj.owner.get_full_name()) owner_link.short_description = 'Владелец' def path_display(self, obj): """Путь папки.""" return obj.get_path() path_display.short_description = 'Путь' @admin.register(MaterialTag) class MaterialTagAdmin(admin.ModelAdmin): """Админ интерфейс для тегов.""" list_display = [ 'name', 'slug', 'color_display', 'materials_count' ] search_fields = ['name', 'slug'] readonly_fields = ['materials_count'] def color_display(self, obj): """Отображение цвета.""" return format_html( '{}', obj.color, obj.color ) color_display.short_description = 'Цвет' @admin.register(MaterialAccess) class MaterialAccessAdmin(admin.ModelAdmin): """Админ интерфейс для логов доступа.""" list_display = [ 'material_link', 'user_link', 'action_badge', 'ip_address', 'created_at' ] list_filter = [ 'action', 'created_at' ] search_fields = [ 'material__title', 'user__email', 'ip_address' ] readonly_fields = [ 'material', 'user', 'action', 'ip_address', 'user_agent', 'created_at' ] def material_link(self, obj): """Ссылка на материал.""" url = reverse('admin:materials_material_change', args=[obj.material.id]) return format_html('{}', url, obj.material.title) material_link.short_description = 'Материал' def user_link(self, obj): """Ссылка на пользователя.""" url = reverse('admin:users_user_change', args=[obj.user.id]) return format_html('{}', url, obj.user.get_full_name()) user_link.short_description = 'Пользователь' def action_badge(self, obj): """Бейдж действия.""" colors = { 'view': '#17a2b8', 'download': '#28a745', 'share': '#ffc107' } return format_html( '{}', colors.get(obj.action, '#000'), obj.get_action_display() ) action_badge.short_description = 'Действие' @admin.register(StorageQuota) class StorageQuotaAdmin(admin.ModelAdmin): """Админ интерфейс для квот хранилища.""" list_display = [ 'user_link', 'total_quota_display', 'used_space_display', 'used_percentage_display', 'available_space_display', 'updated_at' ] search_fields = ['user__email'] readonly_fields = [ 'user', 'used_space', 'created_at', 'updated_at' ] actions = ['recalculate_quotas'] def user_link(self, obj): """Ссылка на пользователя.""" url = reverse('admin:users_user_change', args=[obj.user.id]) return format_html('{}', url, obj.user.get_full_name()) user_link.short_description = 'Пользователь' def total_quota_display(self, obj): """Общая квота.""" return f"{obj.total_quota / (1024*1024*1024):.2f} ГБ" total_quota_display.short_description = 'Квота' def used_space_display(self, obj): """Использовано.""" return f"{obj.used_space / (1024*1024):.2f} МБ" used_space_display.short_description = 'Использовано' def used_percentage_display(self, obj): """Процент использования.""" percentage = obj.get_used_percentage() color = '#28a745' if percentage < 70 else ('#ffc107' if percentage < 90 else '#dc3545') return format_html( '{:.1f}%', color, percentage ) used_percentage_display.short_description = 'Использовано %' def available_space_display(self, obj): """Доступно.""" return f"{obj.get_available_space() / (1024*1024):.2f} МБ" available_space_display.short_description = 'Доступно' @admin.action(description='Пересчитать квоты') def recalculate_quotas(self, request, queryset): """Пересчитать квоты.""" for quota in queryset: quota.recalculate()