Compare commits

..

10 commits

Author SHA1 Message Date
Josh Washburne
b73f3b5bb8 Ability to disable/enable replay variance. 2020-04-11 18:43:05 -04:00
Josh Washburne
9b2da555d1 Moved jingles_enabled to jingles category. 2020-04-11 18:23:35 -04:00
Josh Washburne
3a3881f661 Added better jingle functionality. 2020-04-11 18:17:39 -04:00
Josh Washburne
efcb00c0ec Fix file encoding. 2020-04-11 18:16:09 -04:00
Josh W
c8568335b8 Remove more redundant queries. 2020-03-11 12:15:57 -04:00
Josh W
0a6422afcd Remove redundant query. 2020-03-11 12:07:24 -04:00
Josh W
855c747155 Optimize import and make output user-friendly. 2020-03-11 11:58:50 -04:00
Josh W
17f79d2a4c Use prefetch/select related to optimize. Cleanup. 2020-03-10 14:53:13 -04:00
Josh W
518887e82a No more redundant queries for profile. Cleanup. 2020-03-10 14:51:15 -04:00
Josh W
2fdcb38aa8 Change related names to something more helpful. 2020-03-10 14:50:53 -04:00
11 changed files with 239 additions and 100 deletions

Binary file not shown.

View file

@ -64,19 +64,17 @@ class NextRequest(RetrieveAPIView):
def retrieve(self, request): def retrieve(self, request):
dj_profile = RadioProfile.objects.get(user__is_dj=True) dj_profile = RadioProfile.objects.get(user__is_dj=True)
limit = radio_settings['general__songs_per_jingle'] if SongRequest.music.should_play_jingle():
queued_songs = SongRequest.music.get_queued_requests(limit) random_jingle = Song.music.get_random_jingle()
if [j for j in queued_songs if j.song.is_jingle]: next_request = SongRequest.objects.create(profile=dj_profile,
song=random_jingle)
else:
if not SongRequest.music.new_requests().exists(): if not SongRequest.music.new_requests().exists():
random_song = Song.music.get_random_requestable_song() random_song = Song.music.get_random_requestable_song()
next_request = SongRequest.objects.create(profile=dj_profile, next_request = SongRequest.objects.create(profile=dj_profile,
song=random_song) song=random_song)
else: else:
next_request = SongRequest.music.next_request() next_request = SongRequest.music.next_request()
else:
random_jingle = Song.music.get_random_jingle()
next_request = SongRequest.objects.create(profile=dj_profile,
song=random_jingle)
next_request.queued_at = timezone.now() next_request.queued_at = timezone.now()
next_request.save(update_fields=['queued_at']) next_request.save(update_fields=['queued_at'])
@ -96,9 +94,8 @@ class MakeRequest(APIView):
def post(self, request, format=None): def post(self, request, format=None):
serializer = MakeRequestSerializer(data=request.data) serializer = MakeRequestSerializer(data=request.data)
if serializer.is_valid(): if serializer.is_valid():
profile = RadioProfile.objects.get(user=request.user)
try: try:
profile.make_request(serializer.data['song']) request.user.profile.make_request(serializer.data['song'])
except MakeRequestError as e: except MakeRequestError as e:
return Response({'detail': str(e)}, return Response({'detail': str(e)},
status=status.HTTP_400_BAD_REQUEST) status=status.HTTP_400_BAD_REQUEST)

View file

@ -201,10 +201,9 @@ class SongViewSet(viewsets.ModelViewSet):
def favorite(self, request, pk=None): def favorite(self, request, pk=None):
'''Add a song to the user's favorites list.''' '''Add a song to the user's favorites list.'''
song = self.get_object() song = self.get_object()
profile = RadioProfile.objects.get(user=request.user) if song not in request.user.profile.favorites.all():
if song not in profile.favorites.all(): request.user.profile.favorites.add(song)
profile.favorites.add(song) request.user.profile.save()
profile.save()
return Response({'detail': 'Song has been added to favorites.'}) return Response({'detail': 'Song has been added to favorites.'})
message = 'Song is already a favorite.' message = 'Song is already a favorite.'
return Response({'detail': message}, return Response({'detail': message},
@ -216,10 +215,9 @@ class SongViewSet(viewsets.ModelViewSet):
def unfavorite(self, request, pk=None): def unfavorite(self, request, pk=None):
'''Remove a song from the user's favorites list.''' '''Remove a song from the user's favorites list.'''
song = self.get_object() song = self.get_object()
profile = RadioProfile.objects.get(user=request.user) if song in request.user.profile.favorites.all():
if song in profile.favorites.all(): request.user.profile.favorites.remove(song)
profile.favorites.remove(song) request.user.profile.save()
profile.save()
message = 'Song has been removed from favorites.' message = 'Song has been removed from favorites.'
return Response({'detail': message}) return Response({'detail': message})
message = 'Song is already not a favorite.' message = 'Song is already not a favorite.'
@ -248,10 +246,9 @@ class SongViewSet(viewsets.ModelViewSet):
serializer = RateSongSerializer(data=request.data) serializer = RateSongSerializer(data=request.data)
if serializer.is_valid(): if serializer.is_valid():
song = self.get_object() song = self.get_object()
profile = RadioProfile.objects.get(user=request.user)
if 'value' in serializer.data: if 'value' in serializer.data:
rating, created = Rating.objects.update_or_create( rating, created = Rating.objects.update_or_create(
profile=profile, profile=request.user.profile,
song=song, song=song,
defaults={'value': serializer.data['value']} defaults={'value': serializer.data['value']}
) )
@ -270,8 +267,7 @@ class SongViewSet(viewsets.ModelViewSet):
def unrate(self, request, pk=None): def unrate(self, request, pk=None):
'''Remove a user's rating from a song.''' '''Remove a user's rating from a song.'''
song = self.get_object() song = self.get_object()
profile = RadioProfile.objects.get(user=request.user) rating = song.rating_set.filter(profile=request.user.profile)
rating = song.rating_set.filter(profile=profile)
if rating: if rating:
rating.delete() rating.delete()
return Response({'detail': 'Rating deleted from song.'}) return Response({'detail': 'Rating deleted from song.'})

View file

@ -1,11 +1,16 @@
from django.forms import ValidationError from django.forms import ValidationError
from django.utils import timezone
from dynamic_preferences.types import FloatPreference, IntegerPreference from dynamic_preferences.types import (
BooleanPreference, ChoicePreference, DurationPreference,
FloatPreference, IntegerPreference
)
from dynamic_preferences.preferences import Section from dynamic_preferences.preferences import Section
from dynamic_preferences.registries import global_preferences_registry from dynamic_preferences.registries import global_preferences_registry
general = Section('general') general = Section('general')
jingles = Section('jingles')
replay = Section('replay') replay = Section('replay')
@ -26,9 +31,31 @@ class MaxSongRequests(IntegerPreference):
raise ValidationError('Must be greater than 0.') raise ValidationError('Must be greater than 0.')
@global_preferences_registry.register
class JinglesEnabled(BooleanPreference):
section = jingles
name = 'jingles_enabled'
help_text = 'Enable jingle functionality for the radio.'
default = False
required = False
@global_preferences_registry.register
class JinglesFunction(ChoicePreference):
section = jingles
name = 'jingles_function'
help_text = 'The function to determine when jingles are played.'
choices = [
('after_songs', 'After so many songs are played'),
('after_time', 'After so much time has passed'),
]
default = 'after_songs'
required = True
@global_preferences_registry.register @global_preferences_registry.register
class SongsPerJingle(IntegerPreference): class SongsPerJingle(IntegerPreference):
section = general section = jingles
name = 'songs_per_jingle' name = 'songs_per_jingle'
help_text = 'The amount of songs that will be played between jingles.' help_text = 'The amount of songs that will be played between jingles.'
default = 30 default = 30
@ -39,6 +66,15 @@ class SongsPerJingle(IntegerPreference):
raise ValidationError('Must be greater than 0.') raise ValidationError('Must be greater than 0.')
@global_preferences_registry.register
class TimeBeforeJingle(DurationPreference):
section = jingles
name = 'time_before_jingle'
help_text = 'The amount of time before a jingle is played.'
default = timezone.timedelta(hours=1)
required = True
@global_preferences_registry.register @global_preferences_registry.register
class ReplayRatio(FloatPreference): class ReplayRatio(FloatPreference):
section = replay section = replay
@ -60,6 +96,15 @@ class ReplayRatio(FloatPreference):
raise ValidationError('Must be between 0.0 and 1.0, inclusive.') raise ValidationError('Must be between 0.0 and 1.0, inclusive.')
@global_preferences_registry.register
class VarianceEnabled(BooleanPreference):
section = replay
name = 'variance_enabled'
help_text = 'Enable variance to replay time based on user ratings.'
default = False
required = False
@global_preferences_registry.register @global_preferences_registry.register
class MinRatingsForVariance(IntegerPreference): class MinRatingsForVariance(IntegerPreference):
section = replay section = replay

View file

@ -13,15 +13,11 @@ class RequestSongActionMixin(object):
return actions return actions
def request_song(self, request, obj, parent_obj=None): 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 # This is to get around the M2M 'through' table on Artists
song = obj song = obj.song if not isinstance(obj, Song) else obj
if not isinstance(song, Song):
song = obj.song
try: try:
profile.make_request(song) request.user.profile.make_request(song)
except MakeRequestError as e: except MakeRequestError as e:
return messages.error( return messages.error(
request, request,
@ -30,7 +26,7 @@ class RequestSongActionMixin(object):
return messages.success(request, 'Successfully queued song.') return messages.success(request, 'Successfully queued song.')
def get_request_song_label(self, obj): def get_request_song_label(self, obj):
return 'Request Song' return 'Queue'
class ToggleFavoriteActionsMixin(object): class ToggleFavoriteActionsMixin(object):
@ -41,21 +37,22 @@ class ToggleFavoriteActionsMixin(object):
return actions return actions
def toggle_favorite(self, request, obj, parent_obj=None): def toggle_favorite(self, request, obj, parent_obj=None):
profile = RadioProfile.objects.get(user=request.user)
# This is to get around the M2M 'through' table # This is to get around the M2M 'through' table
song = obj song = obj.song if not isinstance(obj, Song) else obj
if not isinstance(song, Song):
song = obj.song
found = profile.favorites.filter(pk=song.pk).exists() found = request.user.profile.favorites.filter(pk=song.pk).exists()
if found: if found:
profile.favorites.remove(song) request.user.profile.favorites.remove(song)
else: else:
profile.favorites.add(song) request.user.profile.favorites.add(song)
status = 'removed from favorites' if found else 'added to favorites' status = 'removed from favorites' if found else 'added to favorites'
messages.success(request, 'Song successfully {}.'.format(status)) messages.success(request, 'Song successfully {}.'.format(status))
def get_toggle_favorite_label(self, obj): def get_toggle_favorite_label(self, obj):
return 'Toggle Favorite' # This is to get around the M2M 'through' table
song = obj.song if not isinstance(obj, Song) else obj
if self._request.user.profile.favorites.filter(pk=song.pk).exists():
return 'Unfavorite'
return 'Favorite'

View file

@ -1,4 +1,10 @@
from django.db import models from django.db import models
from django.utils import timezone
from dynamic_preferences.registries import global_preferences_registry
radio_settings = global_preferences_registry.manager()
class RequestManager(models.Manager): class RequestManager(models.Manager):
@ -34,3 +40,22 @@ class RequestManager(models.Manager):
def next_request(self): def next_request(self):
return self.new_requests().earliest('created_date') return self.new_requests().earliest('created_date')
def should_play_jingle(self):
if radio_settings['jingles__jingles_enabled']:
function = radio_settings['jingles__jingles_function']
if function == 'after_songs':
limit = radio_settings['jingles__songs_per_jingle']
queued_songs = self.get_queued_requests(limit)
if not [j for j in queued_songs if j.song.is_jingle]:
return True
elif function == 'after_time':
now = timezone.now()
delta = radio_settings['jingles__time_before_jingle']
earlier = now - delta
queued_songs = self.get_queued_requests().filter(
queued_at__range=(earlier, now)
)
if not [j for j in queued_songs if j.song.is_jingle]:
return True
return False

View file

@ -1,4 +1,4 @@
# Generated by Django 2.0.3 on 2018-03-30 15:24 # Generated by Django 3.0.3 on 2020-03-10 14:19
from django.conf import settings from django.conf import settings
import django.core.validators import django.core.validators
@ -11,8 +11,8 @@ class Migration(migrations.Migration):
initial = True initial = True
dependencies = [ dependencies = [
('radio', '0005_replaygain_data'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL), migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('radio', '0002_naming_and_sorting'),
] ]
operations = [ operations = [
@ -25,21 +25,7 @@ class Migration(migrations.Migration):
('disabled_reason', models.TextField(blank=True, verbose_name='reason for disabling')), ('disabled_reason', models.TextField(blank=True, verbose_name='reason for disabling')),
('created_date', models.DateTimeField(auto_now_add=True, verbose_name='added on')), ('created_date', models.DateTimeField(auto_now_add=True, verbose_name='added on')),
('modified_date', models.DateTimeField(auto_now=True, verbose_name='last modified')), ('modified_date', models.DateTimeField(auto_now=True, verbose_name='last modified')),
('favorites', models.ManyToManyField(related_name='song_favorites', to='radio.Song')), ('favorites', models.ManyToManyField(related_name='favorited_by', to='radio.Song')),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='Rating',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created_date', models.DateTimeField(auto_now_add=True, verbose_name='added on')),
('modified_date', models.DateTimeField(auto_now=True, verbose_name='last modified')),
('value', models.PositiveIntegerField(validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(5)], verbose_name='song rating')),
('profile', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='rating_profile', to='profiles.RadioProfile')),
('song', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='radio.Song')),
], ],
options={ options={
'abstract': False, 'abstract': False,
@ -57,22 +43,36 @@ class Migration(migrations.Migration):
('song', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='radio.Song')), ('song', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='radio.Song')),
], ],
options={ options={
'ordering': ['-created_date'], 'ordering': ['-queued_at', '-created_date'],
},
),
migrations.CreateModel(
name='Rating',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created_date', models.DateTimeField(auto_now_add=True, verbose_name='added on')),
('modified_date', models.DateTimeField(auto_now=True, verbose_name='last modified')),
('value', models.PositiveIntegerField(validators=[django.core.validators.MinValueValidator(1), django.core.validators.MaxValueValidator(5)], verbose_name='song rating')),
('profile', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='rating_profile', to='profiles.RadioProfile')),
('song', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='radio.Song')),
],
options={
'abstract': False,
}, },
), ),
migrations.AddField( migrations.AddField(
model_name='radioprofile', model_name='radioprofile',
name='ratings', name='ratings',
field=models.ManyToManyField(related_name='song_ratings', through='profiles.Rating', to='radio.Song'), field=models.ManyToManyField(related_name='ratings', through='profiles.Rating', to='radio.Song'),
), ),
migrations.AddField( migrations.AddField(
model_name='radioprofile', model_name='radioprofile',
name='song_requests', name='song_requests',
field=models.ManyToManyField(related_name='song_requests', through='profiles.SongRequest', to='radio.Song'), field=models.ManyToManyField(related_name='request_history', through='profiles.SongRequest', to='radio.Song'),
), ),
migrations.AddField( migrations.AddField(
model_name='radioprofile', model_name='radioprofile',
name='user', name='user',
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='profile', to=settings.AUTH_USER_MODEL),
), ),
] ]

View file

@ -18,14 +18,15 @@ radio_settings = global_preferences_registry.manager()
class RadioProfile(Disableable, Timestampable, models.Model): class RadioProfile(Disableable, Timestampable, models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, user = models.OneToOneField(settings.AUTH_USER_MODEL,
on_delete=models.CASCADE, on_delete=models.CASCADE,
related_name='profile',
null=True, null=True,
blank=True) blank=True)
favorites = models.ManyToManyField(Song, related_name='song_favorites') favorites = models.ManyToManyField(Song, related_name='favorited_by')
ratings = models.ManyToManyField(Song, ratings = models.ManyToManyField(Song,
related_name='song_ratings', related_name='ratings',
through='Rating') through='Rating')
song_requests = models.ManyToManyField(Song, song_requests = models.ManyToManyField(Song,
related_name='song_requests', related_name='request_history',
through='SongRequest') through='SongRequest')
def disable(self, reason=''): def disable(self, reason=''):

View file

@ -6,9 +6,10 @@ from inline_actions.admin import (
InlineActionsMixin, InlineActionsModelAdminMixin InlineActionsMixin, InlineActionsModelAdminMixin
) )
from core.utils import quantify
from profiles.actions import RequestSongActionMixin, ToggleFavoriteActionsMixin
from .actions import change_items, publish_items, remove_items from .actions import change_items, publish_items, remove_items
from .models import Album, Artist, Game, Song, Store from .models import Album, Artist, Game, Song, Store
from profiles.actions import RequestSongActionMixin, ToggleFavoriteActionsMixin
class BaseSongInline(RequestSongActionMixin, class BaseSongInline(RequestSongActionMixin,
@ -243,7 +244,6 @@ class StoreAdmin(admin.ModelAdmin):
@admin.register(Song) @admin.register(Song)
class SongAdmin(RequestSongActionMixin, class SongAdmin(RequestSongActionMixin,
ToggleFavoriteActionsMixin,
InlineActionsModelAdminMixin, InlineActionsModelAdminMixin,
admin.ModelAdmin): admin.ModelAdmin):
formfield_overrides = { formfield_overrides = {
@ -258,9 +258,10 @@ class SongAdmin(RequestSongActionMixin,
'_is_enabled', '_is_enabled',
'_is_published', '_is_published',
'_is_requestable') '_is_requestable')
list_select_related = True list_select_related = ('album', 'game')
search_fields = ['title'] search_fields = ['title']
actions = ['publish_songs', actions = ['publish_songs',
'add_favorites', 'remove_favorites',
'add_game', 'remove_game', 'add_game', 'remove_game',
'add_album', 'remove_album', 'add_album', 'remove_album',
'add_artists', 'remove_artists'] 'add_artists', 'remove_artists']
@ -300,18 +301,29 @@ class SongAdmin(RequestSongActionMixin,
) )
inlines = [ArtistInline, StoreInline] inlines = [ArtistInline, StoreInline]
def get_queryset(self, request):
qs = super().get_queryset(request)
return qs.prefetch_related('artists', 'stores')
def formfield_for_foreignkey(self, db_field, request, **kwargs): def formfield_for_foreignkey(self, db_field, request, **kwargs):
if db_field.name == 'active_store': 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( kwargs['queryset'] = Store.objects.filter(
song__pk=request.resolver_match.kwargs['object_id'] song__pk=request.resolver_match.kwargs['object_id']
) )
except KeyError:
kwargs['queryset'] = Store.objects.none()
return super().formfield_for_foreignkey(db_field, request, **kwargs) return super().formfield_for_foreignkey(db_field, request, **kwargs)
# Admin Actions
def artist_list(self, obj): def artist_list(self, obj):
return ', '.join([a.full_name for a in obj.artists.all()]) return ', '.join([a.full_name for a in obj.artists.all()])
# Admin Actions
## Metadata CRUD
def add_album(self, request, queryset): def add_album(self, request, queryset):
return change_items(request, queryset, 'album', 'add_album') return change_items(request, queryset, 'album', 'add_album')
add_album.short_description = "Add album to selected items" add_album.short_description = "Add album to selected items"
@ -338,6 +350,28 @@ class SongAdmin(RequestSongActionMixin,
return remove_items(request, queryset, 'game', 'remove_game') return remove_items(request, queryset, 'game', 'remove_game')
remove_game.short_description = "Remove game from selected items" 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.
def publish_songs(self, request, queryset): def publish_songs(self, request, queryset):
publish_items(request, queryset) publish_items(request, queryset)
publish_songs.short_description = "Publish selected songs" publish_songs.short_description = "Publish selected songs"

View file

@ -9,12 +9,16 @@ import os
import re import re
from django.core.management.base import BaseCommand, CommandError from django.core.management.base import BaseCommand, CommandError
from django.utils import timezone
from core.utils import path_to_iri from core.utils import path_to_iri
from radio.models import Album, Artist, Game, Store, Song from radio.models import Album, Artist, Game, Store, Song
decimal.getcontext().prec = 8 decimal.getcontext().prec = 8
PROGRESS = '\rAdding {}: [{:>{width}} / {}]'
COMPLETED = 'Imported {} {}'
class Command(BaseCommand): class Command(BaseCommand):
'''Main "importoldreadio" command class''' '''Main "importoldreadio" command class'''
@ -40,32 +44,62 @@ class Command(BaseCommand):
} }
# Fetching albums first # Fetching albums first
number = len(playlist['albums']) if 'albums' in playlist else 0
self.stdout.write('\n')
for album in playlist['albums']: for album in playlist['albums']:
Album.objects.create(title=album['title'], Album.objects.create(title=album['title'],
disabled=album['disabled']) disabled=album['disabled'])
totals['albums'] += 1 totals['albums'] += 1
self.stdout.write(PROGRESS.format(
self.stdout.write('Imported {} albums'.format(str(totals['albums']))) 'albums',
totals['albums'],
number,
width=len(str(number))
), ending='')
self.stdout.flush()
self.stdout.write('\n')
self.stdout.write(COMPLETED.format(str(totals['albums']), 'albums'))
# Next up, artists # Next up, artists
number = len(playlist['artists']) if 'artists' in playlist else 0
self.stdout.write('\n')
for artist in playlist['artists']: for artist in playlist['artists']:
Artist.objects.create(alias=artist['alias'] or '', Artist.objects.create(alias=artist['alias'] or '',
first_name=artist['first_name'] or '', first_name=artist['first_name'] or '',
last_name=artist['last_name'] or '', last_name=artist['last_name'] or '',
disabled=artist['disabled']) disabled=artist['disabled'])
totals['artists'] += 1 totals['artists'] += 1
self.stdout.write(PROGRESS.format(
self.stdout.write('Imported {} artists'.format(str(totals['artists']))) 'artists',
totals['artists'],
number,
width=len(str(number))
), ending='')
self.stdout.flush()
self.stdout.write('\n')
self.stdout.write(COMPLETED.format(str(totals['artists']), 'artists'))
# On to games # On to games
number = len(playlist['games']) if 'games' in playlist else 0
self.stdout.write('\n')
for game in playlist['games']: for game in playlist['games']:
Game.objects.create(title=game['title'], Game.objects.create(title=game['title'],
disabled=game['disabled']) disabled=game['disabled'])
totals['games'] += 1 totals['games'] += 1
self.stdout.write(PROGRESS.format(
self.stdout.write('Imported {} games'.format(str(totals['games']))) 'games',
totals['games'],
number,
width=len(str(number))
), ending='')
self.stdout.flush()
self.stdout.write('\n')
self.stdout.write(COMPLETED.format(str(totals['games']), 'games'))
# Followed by the songs # Followed by the songs
number = len(playlist['songs']) if 'songs' in playlist else 0
progress = '\rAdding requestables: [{:>{width}} / {}]'
self.stdout.write('\n')
for song in playlist['songs']: for song in playlist['songs']:
try: try:
album = Album.objects.get(title__exact=song['album']) album = Album.objects.get(title__exact=song['album'])
@ -128,26 +162,32 @@ class Command(BaseCommand):
else: else:
totals['jingles'] += 1 totals['jingles'] += 1
self.stdout.write( self.stdout.write(PROGRESS.format(
'Imported {} requestables ({} songs, {} jingles)'.format( 'requestables',
totals['songs'] + totals['jingles'],
number,
width=len(str(number))
), ending='')
self.stdout.flush()
self.stdout.write('\n')
self.stdout.write(COMPLETED.format(
str(totals['songs'] + totals['jingles']), str(totals['songs'] + totals['jingles']),
'requestables ({} songs, {} jingles)'.format(
str(totals['songs']), str(totals['songs']),
str(totals['jingles']) str(totals['jingles'])
) )
) ))
pub = input('Do you want to publish all imported objects as well? ' pub = input('Do you want to publish all imported objects as well? '
'[Y/N] ') '[Y/N] ')
if pub in ('Y', 'y'): if pub in ('Y', 'y'):
for album in Album.objects.all(): date = timezone.now()
album.publish() Album.objects.all().update(published_date=date)
for artist in Artist.objects.all(): Artist.objects.all().update(published_date=date)
artist.publish() Game.objects.all().update(published_date=date)
for game in Game.objects.all(): Song.objects.all().update(published_date=date)
game.publish()
for song in Song.objects.all():
song.publish()
self.stdout.write('Published imported objects successfully') self.stdout.write('Published imported objects successfully')
else: else:
self.stdout.write('Skipped publishing songs') self.stdout.write('Skipped publishing songs')

View file

@ -246,6 +246,20 @@ class Song(Disableable, Publishable, Timestampable, models.Model):
return None return None
average_rating = property(_average_rating) average_rating = property(_average_rating)
def get_adjusted_ratio(self):
'''
Return a change to the replay ratio based on various factors.
'''
if radio_settings['replay__variance_enabled']:
min_ratings = radio_settings['replay__min_ratings_for_variance']
if self.rating_set.count() >= min_ratings:
ratio = radio_settings['replay__rating_variance_ratio']
# -((average - 1)/(highest_rating - 1)) * ratio
base = -((self._average_rating() - 1) / 4) * ratio
return float(base + (ratio * 0.5))
return float(0.0)
def get_time_until_requestable(self): def get_time_until_requestable(self):
''' '''
Length of time before a song can be requested again. Length of time before a song can be requested again.
@ -267,17 +281,7 @@ class Song(Disableable, Publishable, Timestampable, models.Model):
if self._is_song() and self._is_available(): if self._is_song() and self._is_available():
if last: if last:
# Check if we have enough ratings to change ratio adjusted_ratio = self.get_adjusted_ratio()
min_rate = radio_settings['replay__min_ratings_for_variance']
if self.rating_set.count() >= min_rate:
ratio = radio_settings['replay__rating_variance_ratio']
# -((average - 1)/(highest_rating - 1)) * ratio
base = -((self._average_rating() - 1) / 4) * ratio
adjusted_ratio = float(base + (ratio * 0.5))
else:
adjusted_ratio = float(0.0)
return last + Song.music.wait_total(adjusted_ratio) return last + Song.music.wait_total(adjusted_ratio)
return timezone.now() return timezone.now()
return None return None