Implemented rating variance for song requests.
This commit is contained in:
parent
bf88b8cfdc
commit
b93b8751ec
6 changed files with 72 additions and 15 deletions
|
@ -14,6 +14,16 @@ def default_settings(apps, schema_editor):
|
||||||
'not apply to users who are designated as staff.',
|
'not apply to users who are designated as staff.',
|
||||||
setting_type=SETTING_TYPES['Integer'],
|
setting_type=SETTING_TYPES['Integer'],
|
||||||
data='5'),
|
data='5'),
|
||||||
|
Setting(name='min_ratings_for_variance',
|
||||||
|
description='The minimum amount of ratings for the rating '
|
||||||
|
'variance to take effect on the replay ratios.',
|
||||||
|
setting_type=SETTING_TYPES['Integer'],
|
||||||
|
data='5'),
|
||||||
|
Setting(name='rating_variance_ratio',
|
||||||
|
description='The range in which the replay ratio can be '
|
||||||
|
'adjusted due to profile ratings.',
|
||||||
|
setting_type=SETTING_TYPES['Float'],
|
||||||
|
data='0.20'),
|
||||||
Setting(name='replay_ratio',
|
Setting(name='replay_ratio',
|
||||||
description='This defines how long before a song can be '
|
description='This defines how long before a song can be '
|
||||||
'played/requested again once it\'s been played. '
|
'played/requested again once it\'s been played. '
|
||||||
|
@ -21,9 +31,10 @@ def default_settings(apps, schema_editor):
|
||||||
'all the enabled, requestable songs in the radio '
|
'all the enabled, requestable songs in the radio '
|
||||||
'playlist. Example: If the total song length of '
|
'playlist. Example: If the total song length of '
|
||||||
'the radio playlist is 432000 seconds (5 days), '
|
'the radio playlist is 432000 seconds (5 days), '
|
||||||
'then a ratio of 0.75 will mean that a song cannot '
|
'then a ratio of 0.75 will mean that a song '
|
||||||
'be played again for 324000 seconds (0.75 * 432000 '
|
'cannot be played again for 324000 seconds '
|
||||||
'= 324000 seconds = 3 days, 18 hours).',
|
'(0.75 * 432000 = 324000 seconds = 3 days, 18 '
|
||||||
|
'hours).',
|
||||||
setting_type=SETTING_TYPES['Float'],
|
setting_type=SETTING_TYPES['Float'],
|
||||||
data='0.75'),
|
data='0.75'),
|
||||||
Setting(name='songs_per_jingle',
|
Setting(name='songs_per_jingle',
|
||||||
|
|
|
@ -18,12 +18,18 @@ def create_profile(sender, instance, created, **kwargs):
|
||||||
@receiver(post_save, sender=SongRequest)
|
@receiver(post_save, sender=SongRequest)
|
||||||
def update_song_plays(sender, instance, created, update_fields, **kwargs):
|
def update_song_plays(sender, instance, created, update_fields, **kwargs):
|
||||||
"""
|
"""
|
||||||
Update the song data with the latest play time and the number of times
|
Set when a song can be requested again once it's queued. Once played,
|
||||||
|
update the song data with the latest play time and the number of times
|
||||||
played.
|
played.
|
||||||
"""
|
"""
|
||||||
if not created and update_fields:
|
if not created and update_fields:
|
||||||
if 'played_at' in update_fields:
|
|
||||||
song = instance.song
|
song = instance.song
|
||||||
|
if 'played_at' in update_fields:
|
||||||
song.last_played = instance.played_at
|
song.last_played = instance.played_at
|
||||||
song.num_played = F('num_played') + 1
|
song.num_played = F('num_played') + 1
|
||||||
song.save()
|
song.save()
|
||||||
|
if 'queued_at' in update_fields:
|
||||||
|
if song.is_song:
|
||||||
|
queued = instance.queued_at
|
||||||
|
song.next_play = song.get_date_when_requestable(queued)
|
||||||
|
song.save()
|
||||||
|
|
|
@ -113,7 +113,8 @@ class SongAdmin(admin.ModelAdmin):
|
||||||
'album',
|
'album',
|
||||||
'artist_list',
|
'artist_list',
|
||||||
'_is_enabled',
|
'_is_enabled',
|
||||||
'_is_published')
|
'_is_published',
|
||||||
|
'_is_requestable')
|
||||||
search_fields = ['title']
|
search_fields = ['title']
|
||||||
actions = ['publish_songs',
|
actions = ['publish_songs',
|
||||||
'add_game', 'remove_game',
|
'add_game', 'remove_game',
|
||||||
|
@ -126,7 +127,8 @@ class SongAdmin(admin.ModelAdmin):
|
||||||
'last_played',
|
'last_played',
|
||||||
'num_played',
|
'num_played',
|
||||||
'created_date',
|
'created_date',
|
||||||
'modified_date')
|
'modified_date',
|
||||||
|
'next_play')
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
('Song Disabling', {
|
('Song Disabling', {
|
||||||
'classes': ('collapse',),
|
'classes': ('collapse',),
|
||||||
|
@ -144,6 +146,7 @@ class SongAdmin(admin.ModelAdmin):
|
||||||
'modified_date',
|
'modified_date',
|
||||||
'last_played',
|
'last_played',
|
||||||
'num_played',
|
'num_played',
|
||||||
|
'next_play',
|
||||||
'length')
|
'length')
|
||||||
}),
|
}),
|
||||||
('Album', {
|
('Album', {
|
||||||
|
|
|
@ -85,12 +85,13 @@ class SongManager(RadioManager):
|
||||||
length = self.available_songs().aggregate(models.Sum('length'))
|
length = self.available_songs().aggregate(models.Sum('length'))
|
||||||
return length['length__sum']
|
return length['length__sum']
|
||||||
|
|
||||||
def wait_total(self):
|
def wait_total(self, adjusted_ratio=0.0):
|
||||||
"""
|
"""
|
||||||
Default length in seconds before a song can be played again. This is
|
Default length in seconds before a song can be played again. This is
|
||||||
based on the replay ratio set in the application settings.
|
based on the replay ratio set in the application settings.
|
||||||
"""
|
"""
|
||||||
wait = self.playlist_length() * Decimal(get_setting('replay_ratio'))
|
total_ratio = get_setting('replay_ratio') + adjusted_ratio
|
||||||
|
wait = self.playlist_length() * Decimal(total_ratio)
|
||||||
wait = wait.quantize(Decimal('.01'), rounding=ROUND_UP)
|
wait = wait.quantize(Decimal('.01'), rounding=ROUND_UP)
|
||||||
return timedelta(seconds=float(wait))
|
return timedelta(seconds=float(wait))
|
||||||
|
|
||||||
|
@ -107,8 +108,8 @@ class SongManager(RadioManager):
|
||||||
(or at all).
|
(or at all).
|
||||||
"""
|
"""
|
||||||
return self.available_songs().filter(
|
return self.available_songs().filter(
|
||||||
models.Q(last_played__lt=self.datetime_from_wait()) |
|
models.Q(next_play__lt=timezone.now()) |
|
||||||
models.Q(last_played__isnull=True)
|
models.Q(next_play__isnull=True)
|
||||||
)
|
)
|
||||||
|
|
||||||
def requestable(self):
|
def requestable(self):
|
||||||
|
|
18
savepointradio/radio/migrations/0003_song_next_play.py
Normal file
18
savepointradio/radio/migrations/0003_song_next_play.py
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
# Generated by Django 2.1.1 on 2018-09-24 18:02
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('radio', '0002_naming_and_sorting'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='song',
|
||||||
|
name='next_play',
|
||||||
|
field=models.DateTimeField(blank=True, editable=False, null=True, verbose_name='can be played again'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -7,6 +7,7 @@ from django.utils import timezone
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from core.behaviors import Disableable, Publishable, Timestampable
|
from core.behaviors import Disableable, Publishable, Timestampable
|
||||||
|
from core.utils import get_setting
|
||||||
from .managers import RadioManager, SongManager
|
from .managers import RadioManager, SongManager
|
||||||
|
|
||||||
|
|
||||||
|
@ -123,6 +124,10 @@ class Song(Disableable, Publishable, Timestampable, models.Model):
|
||||||
null=True,
|
null=True,
|
||||||
blank=True,
|
blank=True,
|
||||||
editable=False)
|
editable=False)
|
||||||
|
next_play = models.DateTimeField(_('can be played again'),
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
editable=False)
|
||||||
length = models.DecimalField(_('song length (in seconds)'),
|
length = models.DecimalField(_('song length (in seconds)'),
|
||||||
max_digits=8,
|
max_digits=8,
|
||||||
decimal_places=2,
|
decimal_places=2,
|
||||||
|
@ -203,13 +208,26 @@ class Song(Disableable, Publishable, Timestampable, models.Model):
|
||||||
return timedelta(seconds=0)
|
return timedelta(seconds=0)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def get_date_when_requestable(self):
|
def get_date_when_requestable(self, last_play=None):
|
||||||
"""
|
"""
|
||||||
Datetime when a song can be requested again.
|
Datetime when a song can be requested again.
|
||||||
"""
|
"""
|
||||||
|
last = self.last_played if last_play is None else last_play
|
||||||
|
|
||||||
if self._is_song() and self._is_available():
|
if self._is_song() and self._is_available():
|
||||||
if self.last_played:
|
if last:
|
||||||
return self.last_played + Song.music.wait_total()
|
# Check if we have enough ratings to change ratio
|
||||||
|
min_ratings = get_setting('min_ratings_for_variance')
|
||||||
|
if self.rating_set.count() >= min_ratings:
|
||||||
|
rate_ratio = get_setting('rating_variance_ratio')
|
||||||
|
|
||||||
|
# -((average - 1)/(highest_rating - 1)) * rating_ratio
|
||||||
|
base = -((self._average_rating() - 1) / 4) * rate_ratio
|
||||||
|
adjusted_ratio = float(base + (rate_ratio * 0.5))
|
||||||
|
else:
|
||||||
|
adjusted_ratio = float(0.0)
|
||||||
|
|
||||||
|
return last + Song.music.wait_total(adjusted_ratio)
|
||||||
return timezone.now()
|
return timezone.now()
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue