Implemented rating variance for song requests.

This commit is contained in:
Josh Washburne 2019-01-15 15:29:14 -05:00
parent bf88b8cfdc
commit b93b8751ec
6 changed files with 72 additions and 15 deletions

View file

@ -4,7 +4,7 @@ from django.db import migrations, models
def default_settings(apps, schema_editor):
SETTING_TYPES = { 'Integer': 0, 'Float': 1, 'String': 2, 'Bool': 3 }
SETTING_TYPES = {'Integer': 0, 'Float': 1, 'String': 2, 'Bool': 3}
Setting = apps.get_model('core', 'Setting')
db_alias = schema_editor.connection.alias
Setting.objects.using(db_alias).bulk_create([
@ -14,6 +14,16 @@ def default_settings(apps, schema_editor):
'not apply to users who are designated as staff.',
setting_type=SETTING_TYPES['Integer'],
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',
description='This defines how long before a song can be '
'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 '
'playlist. Example: If the total song length of '
'the radio playlist is 432000 seconds (5 days), '
'then a ratio of 0.75 will mean that a song cannot '
'be played again for 324000 seconds (0.75 * 432000 '
'= 324000 seconds = 3 days, 18 hours).',
'then a ratio of 0.75 will mean that a song '
'cannot be played again for 324000 seconds '
'(0.75 * 432000 = 324000 seconds = 3 days, 18 '
'hours).',
setting_type=SETTING_TYPES['Float'],
data='0.75'),
Setting(name='songs_per_jingle',

View file

@ -18,12 +18,18 @@ def create_profile(sender, instance, created, **kwargs):
@receiver(post_save, sender=SongRequest)
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.
"""
if not created and update_fields:
if 'played_at' in update_fields:
song = instance.song
if 'played_at' in update_fields:
song.last_played = instance.played_at
song.num_played = F('num_played') + 1
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()

View file

@ -113,7 +113,8 @@ class SongAdmin(admin.ModelAdmin):
'album',
'artist_list',
'_is_enabled',
'_is_published')
'_is_published',
'_is_requestable')
search_fields = ['title']
actions = ['publish_songs',
'add_game', 'remove_game',
@ -126,7 +127,8 @@ class SongAdmin(admin.ModelAdmin):
'last_played',
'num_played',
'created_date',
'modified_date')
'modified_date',
'next_play')
fieldsets = (
('Song Disabling', {
'classes': ('collapse',),
@ -144,6 +146,7 @@ class SongAdmin(admin.ModelAdmin):
'modified_date',
'last_played',
'num_played',
'next_play',
'length')
}),
('Album', {

View file

@ -85,12 +85,13 @@ class SongManager(RadioManager):
length = self.available_songs().aggregate(models.Sum('length'))
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
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)
return timedelta(seconds=float(wait))
@ -107,8 +108,8 @@ class SongManager(RadioManager):
(or at all).
"""
return self.available_songs().filter(
models.Q(last_played__lt=self.datetime_from_wait()) |
models.Q(last_played__isnull=True)
models.Q(next_play__lt=timezone.now()) |
models.Q(next_play__isnull=True)
)
def requestable(self):

View 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'),
),
]

View file

@ -7,6 +7,7 @@ from django.utils import timezone
from django.utils.translation import ugettext_lazy as _
from core.behaviors import Disableable, Publishable, Timestampable
from core.utils import get_setting
from .managers import RadioManager, SongManager
@ -123,6 +124,10 @@ class Song(Disableable, Publishable, Timestampable, models.Model):
null=True,
blank=True,
editable=False)
next_play = models.DateTimeField(_('can be played again'),
null=True,
blank=True,
editable=False)
length = models.DecimalField(_('song length (in seconds)'),
max_digits=8,
decimal_places=2,
@ -203,13 +208,26 @@ class Song(Disableable, Publishable, Timestampable, models.Model):
return timedelta(seconds=0)
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.
"""
last = self.last_played if last_play is None else last_play
if self._is_song() and self._is_available():
if self.last_played:
return self.last_played + Song.music.wait_total()
if last:
# 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 None