spradio-server-django/savepointradio/radio/admin.py

383 lines
12 KiB
Python
Raw Normal View History

from django.contrib import admin, messages
from django.db import models
from django.forms import TextInput
from inline_actions.admin import (
InlineActionsMixin, InlineActionsModelAdminMixin
)
from core.utils import quantify
from profiles.actions import RequestSongActionMixin, ToggleFavoriteActionsMixin
from .actions import change_items, publish_items, remove_items
2019-06-03 19:03:23 +00:00
from .models import Album, Artist, Game, Song, Store
class BaseSongInline(RequestSongActionMixin,
ToggleFavoriteActionsMixin,
InlineActionsMixin,
admin.TabularInline):
# This is VERY hacky and I hate it. I would love to find something
# a little more DRY. There's this:
# https://gist.github.com/cauethenorio/9db40c59cf406bf328fd
# . . .but it seems to be aimed at ModelAdmins, not Inlines. Maybe a
# refactor is in order when I understand the underlying functionality
# down the road.
2019-06-03 19:03:23 +00:00
readonly_fields = (
'title',
'game',
'album',
'artist_list',
'_is_enabled',
'_is_published',
'_is_requestable'
)
fields = readonly_fields
verbose_name = 'related song'
verbose_name_plural = 'related songs'
extra = 0
def title(self, obj):
song = obj
if not isinstance(song, Song):
song = obj.song
return song.title
title.short_description = 'title'
def game(self, obj):
song = obj
if not isinstance(song, Song):
song = obj.song
return song.game
game.short_description = 'game'
def album(self, obj):
song = obj
if not isinstance(song, Song):
song = obj.song
return song.album
album.short_description = 'album'
def artist_list(self, obj):
song = obj
if not isinstance(song, Song):
song = obj.song
return ', '.join([a.full_name for a in song.artists.all()])
def _is_enabled(self, obj):
song = obj
if not isinstance(song, Song):
song = obj.song
return obj.song.is_enabled
_is_enabled.boolean = True
def _is_published(self, obj):
song = obj
if not isinstance(song, Song):
song = obj.song
return obj.song.is_published
_is_published.boolean = True
def _is_requestable(self, obj):
song = obj
if not isinstance(song, Song):
song = obj.song
return obj.song.is_requestable
_is_requestable.boolean = True
class SongInline(BaseSongInline):
model = Song
class SongArtistInline(BaseSongInline):
model = Song.artists.through
class ArtistInline(admin.TabularInline):
model = Song.artists.through
verbose_name = 'artist'
verbose_name_plural = 'artists'
extra = 0
class StoreInline(admin.TabularInline):
model = Song.stores.through
verbose_name = 'data store'
verbose_name_plural = 'data stores'
extra = 0
@admin.register(Album)
class AlbumAdmin(InlineActionsModelAdminMixin, admin.ModelAdmin):
# Detail List display
list_display = ('title', '_is_enabled', '_is_published')
search_fields = ['title']
2018-01-14 20:21:04 +00:00
actions = ['publish_albums']
inlines = [SongInline]
# Edit Form display
readonly_fields = ('created_date', 'modified_date')
fieldsets = (
('Album Disabling', {
'classes': ('collapse',),
'fields': ('disabled', 'disabled_date', 'disabled_reason')
}),
('Main', {
'fields': ('title', 'published_date')
}),
('Stats', {
'classes': ('collapse',),
'fields': ('created_date', 'modified_date')
})
)
2018-01-14 20:21:04 +00:00
def publish_albums(self, request, queryset):
publish_items(request, queryset)
publish_albums.short_description = "Publish selected albums"
class Media:
css = {
'all': ('css/remove_inline_object_names.css', )
}
@admin.register(Artist)
class ArtistAdmin(InlineActionsModelAdminMixin, admin.ModelAdmin):
# Detail List display
list_display = ('first_name',
'alias',
'last_name',
'_is_enabled',
'_is_published')
search_fields = ['first_name', 'alias', 'last_name']
2018-01-14 20:21:04 +00:00
actions = ['publish_artists']
inlines = [SongArtistInline]
# Edit Form display
readonly_fields = ('created_date', 'modified_date')
fieldsets = (
('Artist Disabling', {
'classes': ('collapse',),
'fields': ('disabled', 'disabled_date', 'disabled_reason')
}),
('Main', {
'fields': ('first_name', 'alias', 'last_name', 'published_date')
}),
('Stats', {
'classes': ('collapse',),
'fields': ('created_date', 'modified_date')
})
)
2018-01-14 20:21:04 +00:00
def publish_artists(self, request, queryset):
publish_items(request, queryset)
publish_artists.short_description = "Publish selected artists"
class Media:
css = {
'all': ('css/remove_inline_object_names.css', )
}
@admin.register(Game)
class GameAdmin(InlineActionsModelAdminMixin, admin.ModelAdmin):
# Detail List display
list_display = ('title', '_is_enabled', '_is_published')
search_fields = ['title']
2018-01-14 20:21:04 +00:00
actions = ['publish_games']
inlines = [SongInline]
# Edit Form display
readonly_fields = ('created_date', 'modified_date')
fieldsets = (
('Game Disabling', {
'classes': ('collapse',),
'fields': ('disabled', 'disabled_date', 'disabled_reason')
}),
('Main', {
'fields': ('title', 'published_date')
}),
('Stats', {
'classes': ('collapse',),
'fields': ('created_date', 'modified_date')
})
)
2018-01-14 20:21:04 +00:00
def publish_games(self, request, queryset):
publish_items(request, queryset)
publish_games.short_description = "Publish selected games"
class Media:
css = {
'all': ('css/remove_inline_object_names.css', )
}
2019-06-03 19:03:23 +00:00
@admin.register(Store)
class StoreAdmin(admin.ModelAdmin):
# Detail List display
list_display = ('iri',
'mime_type',
'file_size',
2019-07-03 15:57:46 +00:00
'length',
'_replaygain')
2019-06-03 19:03:23 +00:00
search_fields = ['iri']
# Edit Form display
readonly_fields = ('created_date', 'modified_date')
fieldsets = (
('Main', {
2019-07-03 15:57:46 +00:00
'fields': ('iri',
'mime_type',
'file_size',
'length',
'track_gain',
'track_peak')
2019-06-03 19:03:23 +00:00
}),
('Stats', {
'classes': ('collapse',),
'fields': ('created_date', 'modified_date')
})
)
@admin.register(Song)
class SongAdmin(RequestSongActionMixin,
InlineActionsModelAdminMixin,
admin.ModelAdmin):
formfield_overrides = {
models.TextField: {'widget': TextInput(attrs={'size': 160, })},
}
# Detail List display
list_display = ('title',
'game',
'album',
'artist_list',
'_is_enabled',
'_is_published',
'_is_requestable')
list_select_related = ('album', 'game')
search_fields = ['title']
actions = ['publish_songs',
'add_favorites', 'remove_favorites',
'add_game', 'remove_game',
'add_album', 'remove_album',
'add_artists', 'remove_artists']
# Edit Form display
exclude = ('artists',)
2019-06-03 19:03:23 +00:00
readonly_fields = ('last_played',
'num_played',
'created_date',
'modified_date',
'next_play')
fieldsets = (
('Song Disabling', {
'classes': ('collapse',),
'fields': ('disabled', 'disabled_date', 'disabled_reason')
}),
('Main', {
'fields': ('song_type',
'title',
2019-06-03 19:03:23 +00:00
'published_date',
'active_store')
}),
('Stats', {
'classes': ('collapse',),
'fields': ('created_date',
'modified_date',
'last_played',
'num_played',
2019-06-03 19:03:23 +00:00
'next_play')
}),
('Album', {
'fields': ('album',)
}),
('Game', {
'fields': ('game',)
})
)
2019-06-03 19:03:23 +00:00
inlines = [ArtistInline, StoreInline]
def get_queryset(self, request):
qs = super().get_queryset(request)
return qs.prefetch_related('artists', 'stores')
2019-06-03 19:03:23 +00:00
def formfield_for_foreignkey(self, db_field, request, **kwargs):
if db_field.name == 'active_store':
# New songs won't have an 'object_id' until they are saved for the
# first time. So on new songs, force an empty QuerySet.
try:
kwargs['queryset'] = Store.objects.filter(
song__pk=request.resolver_match.kwargs['object_id']
)
except KeyError:
kwargs['queryset'] = Store.objects.none()
2019-06-03 19:03:23 +00:00
return super().formfield_for_foreignkey(db_field, request, **kwargs)
def artist_list(self, obj):
return ', '.join([a.full_name for a in obj.artists.all()])
# Admin Actions
## Metadata CRUD
def add_album(self, request, queryset):
return change_items(request, queryset, 'album', 'add_album')
add_album.short_description = "Add album to selected items"
def remove_album(self, request, queryset):
return remove_items(request, queryset, 'album', 'remove_album')
remove_album.short_description = "Remove album from selected items"
2018-01-13 05:39:13 +00:00
def add_artists(self, request, queryset):
return change_items(request, queryset, 'artists', 'add_artists',
m2m='artist')
add_artists.short_description = "Add artists to selected items"
def remove_artists(self, request, queryset):
return change_items(request, queryset, 'artists', 'remove_artists',
m2m='artist', remove=True)
remove_artists.short_description = "Remove artists from selected items"
def add_game(self, request, queryset):
return change_items(request, queryset, 'game', 'add_game')
add_game.short_description = "Add game to selected items"
def remove_game(self, request, queryset):
return remove_items(request, queryset, 'game', 'remove_game')
remove_game.short_description = "Remove game from selected items"
## Allow multiple songs to be favorited by the admin user.
def add_favorites(self, request, queryset):
request.user.profile.favorites.add(*queryset)
message = quantify(len(queryset), queryset.model)
messages.success(
request,
'{} successfully added to favorites.'.format(message)
)
add_favorites.short_description = "Add selected songs to favorites"
def remove_favorites(self, request, queryset):
request.user.profile.favorites.remove(*queryset)
message = quantify(len(queryset), queryset.model)
messages.success(
request,
'{} successfully removed from favorites.'.format(message)
)
remove_favorites.short_description = "Remove selected songs from favorites"
## Publish songs if they are sitting unpublished.
2018-01-14 20:21:04 +00:00
def publish_songs(self, request, queryset):
publish_items(request, queryset)
publish_songs.short_description = "Publish selected songs"
class Media:
css = {
'all': ('css/remove_inline_object_names.css', )
}