Anna Sirota
aeaeee7ff9
This makes it easier to identify which files belong to which extensions (because they can be linked via 4 different ways: as icons, featured images, version files and preview files), and makes this relation obvious at the database level.
217 lines
5.7 KiB
Python
217 lines
5.7 KiB
Python
import logging
|
|
|
|
from django.conf import settings
|
|
from django.contrib import admin
|
|
from django.template.loader import render_to_string
|
|
from django.urls import reverse
|
|
from django.utils.safestring import mark_safe
|
|
import background_task.admin
|
|
import background_task.models
|
|
|
|
from .models import File, FileValidation
|
|
import files.signals
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def schedule_scan(self, request, queryset):
|
|
"""Scan selected files."""
|
|
for instance in queryset:
|
|
files.signals.schedule_scan(instance)
|
|
|
|
|
|
def make_thumbnails(self, request, queryset):
|
|
"""Make thumbnails for selected files."""
|
|
for instance in queryset.filter(type__in=(File.TYPES.IMAGE, File.TYPES.VIDEO)):
|
|
files.tasks.make_thumbnails.task_function(file_id=instance.pk)
|
|
|
|
|
|
class FileValidationInlineAdmin(admin.StackedInline):
|
|
model = FileValidation
|
|
readonly_fields = ('date_created', 'date_modified', 'is_ok', 'results')
|
|
extra = 0
|
|
|
|
def _nope(self, request, obj):
|
|
return False
|
|
|
|
has_add_permission = _nope
|
|
has_change_permission = _nope
|
|
has_delete_permission = _nope
|
|
|
|
|
|
@admin.register(File)
|
|
class FileAdmin(admin.ModelAdmin):
|
|
class Media:
|
|
css = {'all': ('files/admin/file.css',)}
|
|
|
|
def thumbnails(self, obj):
|
|
if not obj or not (obj.is_image or obj.is_video):
|
|
return ''
|
|
try:
|
|
context = {'file': obj, 'MEDIA_URL': settings.MEDIA_URL}
|
|
return render_to_string('files/admin/thumbnails.html', context)
|
|
except Exception:
|
|
# Make sure any exception happening here is always logged
|
|
# (e.g. admin eats exceptions in ModelAdmin properties, making it hard to debug)
|
|
logger.exception('Failed to render thumbnails')
|
|
raise
|
|
|
|
def get_form(self, request, obj=None, **kwargs):
|
|
"""Override metadata help text depending on file type."""
|
|
if obj and (obj.is_image or obj.is_video):
|
|
help_text = 'Additional information about the file, e.g. existing thumbnails.'
|
|
kwargs.update({'help_texts': {'metadata': help_text}})
|
|
return super().get_form(request, obj, **kwargs)
|
|
|
|
view_on_site = False
|
|
save_on_top = True
|
|
|
|
list_filter = (
|
|
'validation__is_ok',
|
|
'type',
|
|
'status',
|
|
'date_status_changed',
|
|
'date_approved',
|
|
('extension', admin.EmptyFieldListFilter),
|
|
)
|
|
list_display = (
|
|
'original_name',
|
|
'extension_link',
|
|
'user',
|
|
'date_created',
|
|
'type',
|
|
'status',
|
|
'is_ok',
|
|
)
|
|
|
|
list_select_related = ('version__extension', 'user')
|
|
|
|
readonly_fields = (
|
|
'id',
|
|
'date_created',
|
|
'date_modified',
|
|
'date_approved',
|
|
'date_status_changed',
|
|
'size_bytes',
|
|
'thumbnails',
|
|
'thumbnail',
|
|
'type',
|
|
'user',
|
|
'original_hash',
|
|
'original_name',
|
|
'hash',
|
|
'content_type',
|
|
)
|
|
search_fields = (
|
|
'^version__extension__slug',
|
|
'^version__extension__name',
|
|
'extensions__slug',
|
|
'extensions__name',
|
|
'original_name',
|
|
'hash',
|
|
'source',
|
|
)
|
|
|
|
fieldsets = (
|
|
(
|
|
None,
|
|
{
|
|
'fields': (
|
|
'id',
|
|
('source', 'thumbnails', 'thumbnail'),
|
|
('type', 'content_type', 'original_name'),
|
|
'status',
|
|
)
|
|
},
|
|
),
|
|
(
|
|
'Dates',
|
|
{
|
|
'fields': (
|
|
'date_created',
|
|
'date_modified',
|
|
'date_status_changed',
|
|
'date_approved',
|
|
)
|
|
},
|
|
),
|
|
(
|
|
'Details',
|
|
{
|
|
'fields': (
|
|
('hash', 'original_hash'),
|
|
'metadata',
|
|
'size_bytes',
|
|
'user',
|
|
),
|
|
},
|
|
),
|
|
)
|
|
|
|
inlines = [FileValidationInlineAdmin]
|
|
actions = [schedule_scan, make_thumbnails]
|
|
|
|
def extension_link(self, obj):
|
|
return (
|
|
mark_safe(
|
|
'<a href="{}" target="_blank">{}</a>'.format(
|
|
reverse('admin:extensions_extension_change', args=(obj.extension_id,)),
|
|
obj.extension,
|
|
)
|
|
)
|
|
if obj.extension_id
|
|
else '-'
|
|
)
|
|
|
|
extension_link.short_description = 'Extension'
|
|
|
|
def is_ok(self, obj):
|
|
return obj.validation.is_ok if hasattr(obj, 'validation') else None
|
|
|
|
is_ok.boolean = True
|
|
|
|
|
|
try:
|
|
admin.site.unregister(background_task.models.Task)
|
|
admin.site.unregister(background_task.models.CompletedTask)
|
|
except admin.site.NotRegistered:
|
|
pass
|
|
|
|
|
|
class TaskMixin:
|
|
"""Modify a few properties of background tasks displayed in admin."""
|
|
|
|
def no_errors(self, obj):
|
|
"""Replace background_task's "has_error".
|
|
|
|
Make Django's red/green boolean icons less confusing
|
|
in the context of "there's an error during task run".
|
|
"""
|
|
return not bool(obj.last_error)
|
|
|
|
no_errors.boolean = True
|
|
|
|
|
|
@admin.register(background_task.models.Task)
|
|
@admin.register(background_task.models.CompletedTask)
|
|
class TaskAdmin(background_task.admin.TaskAdmin, TaskMixin):
|
|
date_hierarchy = 'run_at'
|
|
list_display = [
|
|
'run_at',
|
|
'task_name',
|
|
'task_params',
|
|
'attempts',
|
|
'no_errors',
|
|
'locked_by',
|
|
'locked_by_pid_running',
|
|
]
|
|
list_filter = (
|
|
'task_name',
|
|
'run_at',
|
|
'failed_at',
|
|
'locked_at',
|
|
'attempts',
|
|
'creator_content_type',
|
|
)
|
|
search_fields = ['task_name', 'task_params', 'last_error', 'verbose_name']
|