379 lines
11 KiB
Python
379 lines
11 KiB
Python
"""
|
|
Административная панель для материалов.
|
|
"""
|
|
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('<a href="{}">{}</a>', 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('<a href="{}">{}</a>', 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(
|
|
'<span style="background-color: {}; color: white; padding: 3px 10px; border-radius: 3px;">{}</span>',
|
|
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(
|
|
'<span style="background-color: {}; color: white; padding: 3px 10px; border-radius: 3px;">{}</span>',
|
|
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('<a href="{}">{}</a>', 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(
|
|
'<span style="background-color: {}; color: white; padding: 3px 10px; border-radius: 3px;">{}</span>',
|
|
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('<a href="{}">{}</a>', 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('<a href="{}">{}</a>', 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(
|
|
'<span style="background-color: {}; color: white; padding: 3px 10px; border-radius: 3px;">{}</span>',
|
|
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('<a href="{}">{}</a>', 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(
|
|
'<span style="color: {}; font-weight: bold;">{:.1f}%</span>',
|
|
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()
|