Oleg Komarov
3922f5d868
Part of #74. This PR drops Extension.file field, and introduces a cached_property Extension.file as a temporary replacement for existing code references to the dropped field. VersionFiles is renamed to VersionFile to match naming conventions. All reads and writes go only via VersionFile cross-table, still assuming a single File record per Version. No functional changes are expected, except for one unfortunate side-effect in recording object deletion via LogEntry: Version deletion is recorded twice: 1. during Extension deleting its Version objects via CASCADE 2. during post_delete for VersionFile when the check for the last file is done After this change is deployed, we can start implementing multiple file uploads per Version to support multi-platform builds (generated with `--split-platforms` option). Reviewed-on: #193 Reviewed-by: Anna Sirota <annasirota@noreply.localhost>
187 lines
5.0 KiB
Python
187 lines
5.0 KiB
Python
import logging
|
|
|
|
from django.conf import settings
|
|
from django.contrib import admin
|
|
from django.template.loader import render_to_string
|
|
|
|
from .models import File, FileValidation
|
|
from common.admin import link_to
|
|
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 get_queryset(self, *args, **kwargs):
|
|
q = super().get_queryset(*args, **kwargs)
|
|
return q.prefetch_related(*self.list_prefetch_related)
|
|
|
|
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)
|
|
|
|
date_hierarchy = 'date_created'
|
|
view_on_site = False
|
|
save_on_top = True
|
|
|
|
list_filter = (
|
|
'validation__is_ok',
|
|
'type',
|
|
'status',
|
|
'date_approved',
|
|
'date_created',
|
|
'date_modified',
|
|
'date_status_changed',
|
|
('version', admin.EmptyFieldListFilter),
|
|
('icon_of', admin.EmptyFieldListFilter),
|
|
('featured_image_of', admin.EmptyFieldListFilter),
|
|
('preview', admin.EmptyFieldListFilter),
|
|
)
|
|
list_display = (
|
|
'original_name',
|
|
link_to('user'),
|
|
'date_created',
|
|
'type',
|
|
'status',
|
|
link_to('version', 'version of'),
|
|
link_to('icon_of'),
|
|
link_to('featured_image_of'),
|
|
link_to('preview_set.extension', 'preview of'),
|
|
'is_ok',
|
|
)
|
|
|
|
list_select_related = (
|
|
'user',
|
|
'validation',
|
|
)
|
|
list_prefetch_related = (
|
|
'icon_of',
|
|
'featured_image_of',
|
|
'preview_set__extension',
|
|
'version',
|
|
'version__extension',
|
|
)
|
|
|
|
autocomplete_fields = ['user']
|
|
readonly_fields = (
|
|
'id',
|
|
'date_created',
|
|
'date_modified',
|
|
'date_approved',
|
|
'date_status_changed',
|
|
'size_bytes',
|
|
'thumbnails',
|
|
'thumbnail',
|
|
'type',
|
|
'original_hash',
|
|
'original_name',
|
|
'hash',
|
|
'content_type',
|
|
)
|
|
search_fields = (
|
|
'version__extension__slug',
|
|
'version__extension__name',
|
|
'extensions__slug',
|
|
'extensions__name',
|
|
'original_name',
|
|
'hash',
|
|
'source',
|
|
'user__email',
|
|
'user__full_name',
|
|
'user__username',
|
|
'icon_of__slug',
|
|
'icon_of__name',
|
|
'featured_image_of__slug',
|
|
'featured_image_of__name',
|
|
'preview__extension__slug',
|
|
'preview__extension__name',
|
|
)
|
|
|
|
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 is_ok(self, obj):
|
|
return obj.validation.is_ok if hasattr(obj, 'validation') else None
|
|
|
|
is_ok.boolean = True
|