2020-01-21 19:51:27 +00:00
|
|
|
from django.contrib import admin, messages
|
2017-12-29 14:56:47 +00:00
|
|
|
from django.db import models
|
|
|
|
from django.forms import TextInput
|
|
|
|
|
2020-02-13 16:33:10 +00:00
|
|
|
from inline_actions.admin import (
|
|
|
|
InlineActionsMixin, InlineActionsModelAdminMixin
|
|
|
|
)
|
2020-01-21 19:51:27 +00:00
|
|
|
|
2018-01-15 02:23:26 +00:00
|
|
|
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
|
2020-01-21 19:51:27 +00:00
|
|
|
from profiles.exceptions import MakeRequestError
|
|
|
|
from profiles.models import RadioProfile
|
2017-12-29 14:56:47 +00:00
|
|
|
|
|
|
|
|
2020-02-13 16:33:10 +00:00
|
|
|
class RequestSongActionMixin(object):
|
|
|
|
'''This allows a song to be requested directly from an admin page.'''
|
|
|
|
def get_inline_actions(self, request, obj=None):
|
|
|
|
actions = super().get_inline_actions(request=request, obj=obj)
|
|
|
|
actions.append('request_song')
|
|
|
|
return actions
|
|
|
|
|
|
|
|
def request_song(self, request, obj, parent_obj=None):
|
|
|
|
profile = RadioProfile.objects.get(user=request.user)
|
|
|
|
|
|
|
|
# This is to get around the M2M 'through' table on Artists
|
|
|
|
song = obj
|
|
|
|
if not isinstance(song, Song):
|
|
|
|
song = obj.song
|
|
|
|
|
|
|
|
try:
|
|
|
|
profile.make_request(song)
|
|
|
|
except MakeRequestError as e:
|
|
|
|
return messages.error(
|
|
|
|
request,
|
|
|
|
'Unable to request the song: {}'.format(str(e))
|
|
|
|
)
|
|
|
|
return messages.success(request, 'Successfully queued song.')
|
|
|
|
|
|
|
|
def get_request_song_label(self, obj):
|
|
|
|
return 'Request Song'
|
|
|
|
|
|
|
|
|
2017-12-29 14:56:47 +00:00
|
|
|
class ArtistInline(admin.TabularInline):
|
|
|
|
model = Song.artists.through
|
|
|
|
verbose_name = 'artist'
|
|
|
|
verbose_name_plural = 'artists'
|
|
|
|
extra = 0
|
|
|
|
|
|
|
|
|
2019-06-03 19:03:23 +00:00
|
|
|
class StoreInline(admin.TabularInline):
|
|
|
|
model = Song.stores.through
|
|
|
|
verbose_name = 'data store'
|
|
|
|
verbose_name_plural = 'data stores'
|
|
|
|
extra = 0
|
|
|
|
|
|
|
|
|
2020-02-13 16:33:10 +00:00
|
|
|
class SongInline(RequestSongActionMixin,
|
|
|
|
InlineActionsMixin,
|
|
|
|
admin.TabularInline):
|
|
|
|
model = Song
|
|
|
|
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 artist_list(self, obj):
|
|
|
|
return ', '.join([a.full_name for a in obj.artists.all()])
|
|
|
|
|
|
|
|
class SongArtistInline(SongInline):
|
|
|
|
model = Song.artists.through
|
|
|
|
|
|
|
|
# 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.
|
|
|
|
|
|
|
|
def title(self, obj):
|
|
|
|
return obj.song.title
|
|
|
|
title.short_description = 'title'
|
|
|
|
|
|
|
|
def game(self, obj):
|
|
|
|
return obj.song.game
|
|
|
|
game.short_description = 'game'
|
|
|
|
|
|
|
|
def album(self, obj):
|
|
|
|
return obj.song.album
|
|
|
|
album.short_description = 'album'
|
|
|
|
|
|
|
|
def artist_list(self, obj):
|
|
|
|
return ', '.join([a.full_name for a in obj.song.artists.all()])
|
|
|
|
|
|
|
|
def _is_enabled(self, obj):
|
|
|
|
return obj.song.is_enabled
|
|
|
|
_is_enabled.boolean = True
|
|
|
|
|
|
|
|
def _is_published(self, obj):
|
|
|
|
return obj.song.is_published
|
|
|
|
_is_published.boolean = True
|
|
|
|
|
|
|
|
def _is_requestable(self, obj):
|
|
|
|
return obj.song.is_requestable
|
|
|
|
_is_requestable.boolean = True
|
|
|
|
|
|
|
|
|
2017-12-29 14:56:47 +00:00
|
|
|
@admin.register(Album)
|
2020-02-13 16:33:10 +00:00
|
|
|
class AlbumAdmin(InlineActionsModelAdminMixin, admin.ModelAdmin):
|
2017-12-29 14:56:47 +00:00
|
|
|
# Detail List display
|
2018-01-04 21:28:59 +00:00
|
|
|
list_display = ('title', '_is_enabled', '_is_published')
|
2017-12-29 14:56:47 +00:00
|
|
|
search_fields = ['title']
|
2018-01-14 20:21:04 +00:00
|
|
|
actions = ['publish_albums']
|
2020-02-13 16:33:10 +00:00
|
|
|
inlines = [SongInline]
|
2017-12-29 14:56:47 +00:00
|
|
|
|
|
|
|
# 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"
|
2017-12-29 14:56:47 +00:00
|
|
|
|
2020-02-13 16:33:10 +00:00
|
|
|
class Media:
|
|
|
|
css = {
|
|
|
|
'all': ('css/remove_inline_object_names.css', )
|
|
|
|
}
|
|
|
|
|
2017-12-29 14:56:47 +00:00
|
|
|
|
|
|
|
@admin.register(Artist)
|
2020-02-13 16:33:10 +00:00
|
|
|
class ArtistAdmin(InlineActionsModelAdminMixin, admin.ModelAdmin):
|
2017-12-29 14:56:47 +00:00
|
|
|
# Detail List display
|
|
|
|
list_display = ('first_name',
|
|
|
|
'alias',
|
|
|
|
'last_name',
|
2018-01-04 21:28:59 +00:00
|
|
|
'_is_enabled',
|
|
|
|
'_is_published')
|
2017-12-29 14:56:47 +00:00
|
|
|
search_fields = ['first_name', 'alias', 'last_name']
|
2018-01-14 20:21:04 +00:00
|
|
|
actions = ['publish_artists']
|
2020-02-13 16:33:10 +00:00
|
|
|
inlines = [SongArtistInline]
|
2017-12-29 14:56:47 +00:00
|
|
|
|
|
|
|
# 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"
|
2017-12-29 14:56:47 +00:00
|
|
|
|
2020-02-13 16:33:10 +00:00
|
|
|
class Media:
|
|
|
|
css = {
|
|
|
|
'all': ('css/remove_inline_object_names.css', )
|
|
|
|
}
|
|
|
|
|
2017-12-29 14:56:47 +00:00
|
|
|
|
|
|
|
@admin.register(Game)
|
2020-02-13 16:33:10 +00:00
|
|
|
class GameAdmin(InlineActionsModelAdminMixin, admin.ModelAdmin):
|
2017-12-29 14:56:47 +00:00
|
|
|
# Detail List display
|
2018-01-04 21:28:59 +00:00
|
|
|
list_display = ('title', '_is_enabled', '_is_published')
|
2017-12-29 14:56:47 +00:00
|
|
|
search_fields = ['title']
|
2018-01-14 20:21:04 +00:00
|
|
|
actions = ['publish_games']
|
2020-02-13 16:33:10 +00:00
|
|
|
inlines = [SongInline]
|
2017-12-29 14:56:47 +00:00
|
|
|
|
|
|
|
# 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"
|
2017-12-29 14:56:47 +00:00
|
|
|
|
2020-02-13 16:33:10 +00:00
|
|
|
class Media:
|
|
|
|
css = {
|
|
|
|
'all': ('css/remove_inline_object_names.css', )
|
|
|
|
}
|
|
|
|
|
2017-12-29 14:56:47 +00:00
|
|
|
|
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')
|
|
|
|
})
|
|
|
|
)
|
|
|
|
|
|
|
|
|
2017-12-29 14:56:47 +00:00
|
|
|
@admin.register(Song)
|
2020-02-13 16:33:10 +00:00
|
|
|
class SongAdmin(RequestSongActionMixin,
|
|
|
|
InlineActionsModelAdminMixin,
|
|
|
|
admin.ModelAdmin):
|
2017-12-29 14:56:47 +00:00
|
|
|
formfield_overrides = {
|
|
|
|
models.TextField: {'widget': TextInput(attrs={'size': 160, })},
|
|
|
|
}
|
|
|
|
|
|
|
|
# Detail List display
|
|
|
|
list_display = ('title',
|
|
|
|
'game',
|
2018-01-12 21:35:25 +00:00
|
|
|
'album',
|
2017-12-29 14:56:47 +00:00
|
|
|
'artist_list',
|
2018-01-04 21:28:59 +00:00
|
|
|
'_is_enabled',
|
2019-01-15 20:29:14 +00:00
|
|
|
'_is_published',
|
|
|
|
'_is_requestable')
|
2020-02-13 16:33:10 +00:00
|
|
|
list_select_related = True
|
2017-12-29 14:56:47 +00:00
|
|
|
search_fields = ['title']
|
2018-01-15 02:23:26 +00:00
|
|
|
actions = ['publish_songs',
|
|
|
|
'add_game', 'remove_game',
|
|
|
|
'add_album', 'remove_album',
|
|
|
|
'add_artists', 'remove_artists']
|
2017-12-29 14:56:47 +00:00
|
|
|
|
|
|
|
# Edit Form display
|
|
|
|
exclude = ('artists',)
|
2019-06-03 19:03:23 +00:00
|
|
|
readonly_fields = ('last_played',
|
2017-12-29 14:56:47 +00:00
|
|
|
'num_played',
|
|
|
|
'created_date',
|
2019-01-15 20:29:14 +00:00
|
|
|
'modified_date',
|
|
|
|
'next_play')
|
2017-12-29 14:56:47 +00:00
|
|
|
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',
|
2019-06-04 16:22:58 +00:00
|
|
|
'active_store')
|
2017-12-29 14:56:47 +00:00
|
|
|
}),
|
|
|
|
('Stats', {
|
|
|
|
'classes': ('collapse',),
|
|
|
|
'fields': ('created_date',
|
|
|
|
'modified_date',
|
|
|
|
'last_played',
|
|
|
|
'num_played',
|
2019-06-03 19:03:23 +00:00
|
|
|
'next_play')
|
2017-12-29 14:56:47 +00:00
|
|
|
}),
|
|
|
|
('Album', {
|
|
|
|
'fields': ('album',)
|
|
|
|
}),
|
|
|
|
('Game', {
|
|
|
|
'fields': ('game',)
|
|
|
|
})
|
|
|
|
)
|
2019-06-03 19:03:23 +00:00
|
|
|
inlines = [ArtistInline, StoreInline]
|
|
|
|
|
|
|
|
def formfield_for_foreignkey(self, db_field, request, **kwargs):
|
2019-06-04 16:22:58 +00:00
|
|
|
if db_field.name == 'active_store':
|
2019-06-03 19:03:23 +00:00
|
|
|
kwargs['queryset'] = Store.objects.filter(
|
|
|
|
song__pk=request.resolver_match.kwargs['object_id']
|
|
|
|
)
|
|
|
|
return super().formfield_for_foreignkey(db_field, request, **kwargs)
|
2017-12-29 14:56:47 +00:00
|
|
|
|
2020-01-21 19:51:27 +00:00
|
|
|
# Admin Actions
|
|
|
|
|
2017-12-29 14:56:47 +00:00
|
|
|
def artist_list(self, obj):
|
|
|
|
return ', '.join([a.full_name for a in obj.artists.all()])
|
|
|
|
|
2018-01-15 02:23:26 +00:00
|
|
|
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):
|
2018-01-15 02:23:26 +00:00
|
|
|
return change_items(request, queryset, 'artists', 'add_artists',
|
|
|
|
m2m='artist')
|
2018-01-12 21:35:25 +00:00
|
|
|
add_artists.short_description = "Add artists to selected items"
|
|
|
|
|
|
|
|
def remove_artists(self, request, queryset):
|
2018-01-15 02:23:26 +00:00
|
|
|
return change_items(request, queryset, 'artists', 'remove_artists',
|
|
|
|
m2m='artist', remove=True)
|
2018-01-12 21:35:25 +00:00
|
|
|
remove_artists.short_description = "Remove artists from selected items"
|
|
|
|
|
2018-01-15 02:23:26 +00:00
|
|
|
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"
|
|
|
|
|
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"
|
2020-01-21 19:51:27 +00:00
|
|
|
|
2020-02-13 16:33:10 +00:00
|
|
|
class Media:
|
|
|
|
css = {
|
|
|
|
'all': ('css/remove_inline_object_names.css', )
|
|
|
|
}
|