Added dynamic settings and removed my own attempt.
This commit is contained in:
parent
6e4cccb5f6
commit
ccddf6c8bf
11 changed files with 122 additions and 145 deletions
|
@ -3,8 +3,10 @@ asgiref>=3.2.3
|
|||
cffi>=1.13.2
|
||||
dj-database-url>=0.5.0
|
||||
Django>=3.0.2
|
||||
django-dynamic-preferences>=1.8.1
|
||||
django-inline-actions>=2.3.0
|
||||
djangorestframework>=3.11.0
|
||||
persisting-theory>=0.2.1
|
||||
psycopg2-binary>=2.8.4
|
||||
pycparser>=2.19
|
||||
python-decouple>=3.3
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
from django.contrib.auth import get_user_model
|
||||
from django.utils import timezone
|
||||
|
||||
from dynamic_preferences.registries import global_preferences_registry
|
||||
from rest_framework import status
|
||||
from rest_framework.authentication import (SessionAuthentication,
|
||||
TokenAuthentication)
|
||||
|
@ -10,7 +11,6 @@ from rest_framework.renderers import BrowsableAPIRenderer, JSONRenderer
|
|||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
|
||||
from core.utils import get_setting
|
||||
from profiles.exceptions import MakeRequestError
|
||||
from profiles.models import RadioProfile, SongRequest
|
||||
from radio.models import Song
|
||||
|
@ -22,6 +22,7 @@ from ..serializers.controls import (JustPlayedSerializer,
|
|||
|
||||
|
||||
User = get_user_model()
|
||||
radio_settings = global_preferences_registry.manager()
|
||||
|
||||
|
||||
class JustPlayed(APIView):
|
||||
|
@ -63,7 +64,7 @@ class NextRequest(RetrieveAPIView):
|
|||
def retrieve(self, request):
|
||||
dj_profile = RadioProfile.objects.get(user__is_dj=True)
|
||||
|
||||
limit = get_setting('songs_per_jingle')
|
||||
limit = radio_settings['general__songs_per_jingle']
|
||||
queued_songs = SongRequest.music.get_queued_requests(limit)
|
||||
if [j for j in queued_songs if j.song.is_jingle]:
|
||||
if not SongRequest.music.new_requests().exists():
|
||||
|
|
92
savepointradio/core/dynamic_preferences_registry.py
Normal file
92
savepointradio/core/dynamic_preferences_registry.py
Normal file
|
@ -0,0 +1,92 @@
|
|||
from django.forms import ValidationError
|
||||
|
||||
from dynamic_preferences.types import FloatPreference, IntegerPreference
|
||||
from dynamic_preferences.preferences import Section
|
||||
from dynamic_preferences.registries import global_preferences_registry
|
||||
|
||||
|
||||
general = Section('general')
|
||||
replay = Section('replay')
|
||||
|
||||
|
||||
@global_preferences_registry.register
|
||||
class MaxSongRequests(IntegerPreference):
|
||||
section = general
|
||||
name = 'max_song_requests'
|
||||
help_text = (
|
||||
'The maximum amount of requests a user can have queued at any given '
|
||||
'time. This restriction does not apply to users who are designated '
|
||||
'as staff.'
|
||||
)
|
||||
default = 5
|
||||
required = True
|
||||
|
||||
def validate(self, value):
|
||||
if value < 0:
|
||||
raise ValidationError('Must be greater than 0.')
|
||||
|
||||
|
||||
@global_preferences_registry.register
|
||||
class SongsPerJingle(IntegerPreference):
|
||||
section = general
|
||||
name = 'songs_per_jingle'
|
||||
help_text = 'The amount of songs that will be played between jingles.'
|
||||
default = 30
|
||||
required = True
|
||||
|
||||
def validate(self, value):
|
||||
if value < 0:
|
||||
raise ValidationError('Must be greater than 0.')
|
||||
|
||||
|
||||
@global_preferences_registry.register
|
||||
class ReplayRatio(FloatPreference):
|
||||
section = replay
|
||||
name = 'replay_ratio'
|
||||
help_text = (
|
||||
'This defines how long before a song can be played/requested again '
|
||||
'once it\'s been played. The ratio is based on the total song length '
|
||||
'of all the enabled, requestable songs in the radio playlist.\n\n'
|
||||
'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).'
|
||||
)
|
||||
default = float(0.75)
|
||||
required = True
|
||||
|
||||
def validate(self, value):
|
||||
if value < 0.0 or value > 1.0:
|
||||
raise ValidationError('Must be between 0.0 and 1.0, inclusive.')
|
||||
|
||||
|
||||
@global_preferences_registry.register
|
||||
class MinRatingsForVariance(IntegerPreference):
|
||||
section = replay
|
||||
name = 'min_ratings_for_variance'
|
||||
help_text = (
|
||||
'The minimum amount of ratings for the rating variance to take effect '
|
||||
'on the replay ratios.'
|
||||
)
|
||||
default = 5
|
||||
required = True
|
||||
|
||||
def validate(self, value):
|
||||
if value < 0:
|
||||
raise ValidationError('Must be greater than 0.')
|
||||
|
||||
|
||||
@global_preferences_registry.register
|
||||
class RatingVarianceRatio(FloatPreference):
|
||||
section = replay
|
||||
name = 'rating_variance_ratio'
|
||||
help_text = (
|
||||
'The range in which the replay ratio can be adjusted due to profile '
|
||||
'ratings.'
|
||||
)
|
||||
default = float(0.20)
|
||||
required = True
|
||||
|
||||
def validate(self, value):
|
||||
if value < 0.0 or value > 1.0:
|
||||
raise ValidationError('Must be between 0.0 and 1.0, inclusive.')
|
|
@ -1,4 +1,4 @@
|
|||
# Generated by Django 3.0.2 on 2020-01-21 16:49
|
||||
# Generated by Django 3.0.3 on 2020-02-16 01:40
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.utils.timezone
|
||||
|
@ -13,16 +13,6 @@ class Migration(migrations.Migration):
|
|||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Setting',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=64, unique=True, verbose_name='name')),
|
||||
('description', models.TextField(blank=True, verbose_name='description')),
|
||||
('setting_type', models.PositiveIntegerField(choices=[(0, 'Integer'), (1, 'Float'), (2, 'String'), (3, 'Bool')], default=0, verbose_name='variable type')),
|
||||
('data', models.TextField(verbose_name='data')),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='RadioUser',
|
||||
fields=[
|
||||
|
|
|
@ -1,56 +0,0 @@
|
|||
# Generated by Django 2.0 on 2017-12-28 15:16
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
def default_settings(apps, schema_editor):
|
||||
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([
|
||||
Setting(name='max_song_requests',
|
||||
description='The maximum amount of requests a user can have '
|
||||
'queued at any given time. This restriction does '
|
||||
'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. '
|
||||
'The ratio is based on the total song length of '
|
||||
'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).',
|
||||
setting_type=SETTING_TYPES['Float'],
|
||||
data='0.75'),
|
||||
Setting(name='songs_per_jingle',
|
||||
description='The amount of songs that will be played between '
|
||||
'jingles.',
|
||||
setting_type=SETTING_TYPES['Integer'],
|
||||
data='30'),
|
||||
])
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('core', '0002_create_dj_user'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(default_settings),
|
||||
]
|
|
@ -56,39 +56,3 @@ class RadioUser(AbstractBaseUser, PermissionsMixin):
|
|||
name=self.name,
|
||||
email=self.email,
|
||||
)
|
||||
|
||||
|
||||
class Setting(models.Model):
|
||||
"""
|
||||
A model for keeping track of dynamic settings while the site is online and
|
||||
the radio is running.
|
||||
"""
|
||||
INTEGER = 0
|
||||
FLOAT = 1
|
||||
STRING = 2
|
||||
BOOL = 3
|
||||
TYPE_CHOICES = (
|
||||
(INTEGER, 'Integer'),
|
||||
(FLOAT, 'Float'),
|
||||
(STRING, 'String'),
|
||||
(BOOL, 'Bool'),
|
||||
)
|
||||
name = models.CharField(_('name'), max_length=64, unique=True)
|
||||
description = models.TextField(_('description'), blank=True)
|
||||
setting_type = models.PositiveIntegerField(_('variable type'),
|
||||
choices=TYPE_CHOICES,
|
||||
default=INTEGER)
|
||||
data = models.TextField(_('data'))
|
||||
|
||||
def get(self):
|
||||
if self.setting_type == self.INTEGER:
|
||||
return int(self.data)
|
||||
elif self.setting_type == self.FLOAT:
|
||||
return float(self.data)
|
||||
elif self.setting_type == self.BOOL:
|
||||
return self.data == 'True'
|
||||
else:
|
||||
return self.data
|
||||
|
||||
def __str__(self):
|
||||
return '{}: {}'.format(self.name, self.data)
|
||||
|
|
|
@ -14,8 +14,6 @@ from urllib.request import pathname2url, url2pathname
|
|||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from django.utils.encoding import iri_to_uri, uri_to_iri
|
||||
|
||||
from .models import Setting
|
||||
|
||||
|
||||
GROUP_NT_UNC = r'file://[A-Za-z0-9!@#$%^&\'\)\(\.\-_{}~]+/'
|
||||
|
||||
|
@ -34,32 +32,6 @@ FILE_IRI_PATTERN = (
|
|||
)
|
||||
|
||||
|
||||
def get_setting(name):
|
||||
'''Helper function to get dynamic settings from the database.'''
|
||||
setting = Setting.objects.get(name=name)
|
||||
return setting.get()
|
||||
|
||||
|
||||
def set_setting(name, value, setting_type=None):
|
||||
'''Helper function to set dynamic settings from the database.'''
|
||||
setting_types = {'Integer': 0, 'Float': 1, 'String': 2, 'Bool': 3}
|
||||
try:
|
||||
setting = Setting.objects.get(name=name)
|
||||
setting.data = str(value)
|
||||
if setting_type in setting_types:
|
||||
setting.setting_type = setting_types[setting_type]
|
||||
setting.save()
|
||||
except ObjectDoesNotExist:
|
||||
if setting_type in setting_types:
|
||||
Setting.objects.create(name=name,
|
||||
setting_type=setting_types[setting_type],
|
||||
data=str(value))
|
||||
else:
|
||||
error_msg = 'New settings need type (Integer, Float, String, Bool)'
|
||||
raise TypeError(error_msg)
|
||||
return
|
||||
|
||||
|
||||
def naturalize(text):
|
||||
'''
|
||||
Return a normalized unicode string, with removed starting articles, for use
|
||||
|
|
|
@ -4,13 +4,17 @@ from django.core.validators import (MaxLengthValidator, MinValueValidator,
|
|||
from django.db import models
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from dynamic_preferences.registries import global_preferences_registry
|
||||
|
||||
from core.behaviors import Disableable, Timestampable
|
||||
from core.utils import get_setting
|
||||
from radio.models import Song
|
||||
from .exceptions import MakeRequestError
|
||||
from .managers import RequestManager
|
||||
|
||||
|
||||
radio_settings = global_preferences_registry.manager()
|
||||
|
||||
|
||||
class RadioProfile(Disableable, Timestampable, models.Model):
|
||||
user = models.OneToOneField(settings.AUTH_USER_MODEL,
|
||||
on_delete=models.CASCADE,
|
||||
|
@ -38,7 +42,7 @@ class RadioProfile(Disableable, Timestampable, models.Model):
|
|||
|
||||
def has_reached_request_max(self):
|
||||
self_requests = SongRequest.music.unplayed().filter(profile=self)
|
||||
max_requests = get_setting('max_song_requests')
|
||||
max_requests = radio_settings['general__max_song_requests']
|
||||
return self_requests.count() >= max_requests
|
||||
|
||||
def can_request(self):
|
||||
|
@ -59,7 +63,7 @@ class RadioProfile(Disableable, Timestampable, models.Model):
|
|||
raise MakeRequestError('User is currently disabled.')
|
||||
|
||||
if self.has_reached_request_max() and not self.user.is_staff:
|
||||
max_requests = get_setting('max_song_requests')
|
||||
max_requests = radio_settings['general__max_song_requests']
|
||||
message = 'User has reached the maximum request limit ({}).'
|
||||
raise MakeRequestError(message.format(max_requests))
|
||||
|
||||
|
|
|
@ -10,13 +10,16 @@ from django.apps import apps
|
|||
from django.db import models
|
||||
from django.utils import timezone
|
||||
|
||||
from core.utils import get_setting
|
||||
from dynamic_preferences.registries import global_preferences_registry
|
||||
|
||||
from .querysets import RadioQuerySet, SongQuerySet
|
||||
|
||||
|
||||
# Set decimal precision
|
||||
getcontext().prec = 16
|
||||
|
||||
radio_settings = global_preferences_registry.manager()
|
||||
|
||||
|
||||
class RadioManager(models.Manager):
|
||||
'''
|
||||
|
@ -97,7 +100,8 @@ class SongManager(RadioManager):
|
|||
Default length in seconds before a song can be played again. This is
|
||||
based on the replay ratio set in the application settings.
|
||||
'''
|
||||
total_ratio = get_setting('replay_ratio') + adjusted_ratio
|
||||
replay_ratio = radio_settings['replay__replay_ratio']
|
||||
total_ratio = replay_ratio + adjusted_ratio
|
||||
wait = self.playlist_length() * Decimal(total_ratio)
|
||||
wait = wait.quantize(Decimal('.01'), rounding=ROUND_UP)
|
||||
return timedelta(seconds=float(wait))
|
||||
|
|
|
@ -11,8 +11,9 @@ from django.db import models
|
|||
from django.utils import timezone
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from dynamic_preferences.registries import global_preferences_registry
|
||||
|
||||
from core.behaviors import Disableable, Publishable, Timestampable
|
||||
from core.utils import get_setting
|
||||
from .fields import RadioIRIField
|
||||
from .managers import RadioManager, SongManager
|
||||
|
||||
|
@ -20,6 +21,8 @@ from .managers import RadioManager, SongManager
|
|||
# Set decimal precision
|
||||
getcontext().prec = 16
|
||||
|
||||
radio_settings = global_preferences_registry.manager()
|
||||
|
||||
|
||||
class Album(Disableable, Publishable, Timestampable, models.Model):
|
||||
'''
|
||||
|
@ -265,13 +268,13 @@ class Song(Disableable, Publishable, Timestampable, models.Model):
|
|||
if self._is_song() and self._is_available():
|
||||
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')
|
||||
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)) * rating_ratio
|
||||
base = -((self._average_rating() - 1) / 4) * rate_ratio
|
||||
adjusted_ratio = float(base + (rate_ratio * 0.5))
|
||||
# -((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)
|
||||
|
||||
|
|
|
@ -57,6 +57,7 @@ INSTALLED_APPS = [
|
|||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
|
||||
'dynamic_preferences',
|
||||
'rest_framework',
|
||||
'rest_framework.authtoken',
|
||||
|
||||
|
|
Loading…
Reference in a new issue