forked from rc/aircox
bkp before branching
This commit is contained in:
@ -18,13 +18,6 @@ class StreamInline(admin.TabularInline):
|
||||
model = Stream
|
||||
extra = 1
|
||||
|
||||
class NameableAdmin(admin.ModelAdmin):
|
||||
fields = [ 'name' ]
|
||||
|
||||
list_display = ['id', 'name']
|
||||
list_filter = []
|
||||
search_fields = ['name',]
|
||||
|
||||
|
||||
@admin.register(Stream)
|
||||
class StreamAdmin(admin.ModelAdmin):
|
||||
@ -32,15 +25,19 @@ class StreamAdmin(admin.ModelAdmin):
|
||||
|
||||
|
||||
@admin.register(Program)
|
||||
class ProgramAdmin(NameableAdmin):
|
||||
class ProgramAdmin(admin.ModelAdmin):
|
||||
def schedule(self, obj):
|
||||
return Schedule.objects.filter(program = obj).count() > 0
|
||||
return Schedule.objects.filter(program=obj).count() > 0
|
||||
|
||||
schedule.boolean = True
|
||||
schedule.short_description = _("Schedule")
|
||||
|
||||
list_display = ('id', 'name', 'active', 'schedule', 'sync', 'station')
|
||||
fields = NameableAdmin.fields + [ 'active', 'station','sync' ]
|
||||
inlines = [ ScheduleInline, StreamInline ]
|
||||
list_display = ('name', 'id', 'active', 'schedule', 'sync', 'station')
|
||||
fields = ['name', 'slug', 'active', 'station', 'sync']
|
||||
prepopulated_fields = {'slug': ('name',)}
|
||||
search_fields = ['name']
|
||||
|
||||
inlines = [ScheduleInline, StreamInline]
|
||||
|
||||
|
||||
@admin.register(Schedule)
|
||||
@ -64,7 +61,6 @@ class ScheduleAdmin(admin.ModelAdmin):
|
||||
'time', 'duration', 'timezone', 'rerun']
|
||||
list_editable = ['time', 'timezone', 'duration']
|
||||
|
||||
|
||||
def get_readonly_fields(self, request, obj=None):
|
||||
if obj:
|
||||
return ['program', 'date', 'frequency']
|
||||
@ -79,6 +75,7 @@ class PortInline(admin.StackedInline):
|
||||
|
||||
@admin.register(Station)
|
||||
class StationAdmin(admin.ModelAdmin):
|
||||
prepopulated_fields = {'slug': ('name',)}
|
||||
inlines = [PortInline]
|
||||
|
||||
|
||||
|
@ -2,19 +2,17 @@ from django.contrib import admin
|
||||
from django.utils.translation import ugettext as _, ugettext_lazy
|
||||
|
||||
from aircox.models import Sound
|
||||
from .base import NameableAdmin
|
||||
from .playlist import TracksInline
|
||||
|
||||
|
||||
@admin.register(Sound)
|
||||
class SoundAdmin(NameableAdmin):
|
||||
class SoundAdmin(admin.ModelAdmin):
|
||||
fields = None
|
||||
list_display = ['id', 'name', 'program', 'type', 'duration', 'mtime',
|
||||
'public', 'good_quality', 'path']
|
||||
list_filter = ('program', 'type', 'good_quality', 'public')
|
||||
fieldsets = [
|
||||
(None, {'fields': NameableAdmin.fields +
|
||||
['path', 'type', 'program', 'diffusion']}),
|
||||
(None, {'fields': ['name', 'path', 'type', 'program', 'diffusion']}),
|
||||
(None, {'fields': ['embed', 'duration', 'public', 'mtime']}),
|
||||
(None, {'fields': ['good_quality']})
|
||||
]
|
||||
|
135
aircox/models.py
135
aircox/models.py
@ -11,9 +11,9 @@ from django.contrib.contenttypes.fields import (GenericForeignKey,
|
||||
GenericRelation)
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.db import models
|
||||
from django.db.models import Q
|
||||
from django.db.models import F, Q
|
||||
from django.db.models.functions import Concat, Substr
|
||||
from django.db.transaction import atomic
|
||||
from django.template.defaultfilters import slugify
|
||||
from django.utils import timezone as tz
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.html import strip_tags
|
||||
@ -26,28 +26,6 @@ from taggit.managers import TaggableManager
|
||||
logger = logging.getLogger('aircox.core')
|
||||
|
||||
|
||||
class Nameable(models.Model):
|
||||
name = models.CharField(
|
||||
_('name'),
|
||||
max_length=128,
|
||||
)
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
@property
|
||||
def slug(self):
|
||||
"""
|
||||
Slug based on the name. We replace '-' by '_'
|
||||
"""
|
||||
return slugify(self.name).replace('-', '_')
|
||||
|
||||
def __str__(self):
|
||||
# if self.pk:
|
||||
# return '#{} {}'.format(self.pk, self.name)
|
||||
return '{}'.format(self.name)
|
||||
|
||||
|
||||
#
|
||||
# Station related classes
|
||||
#
|
||||
@ -67,7 +45,7 @@ def default_station():
|
||||
return Station.objects.default()
|
||||
|
||||
|
||||
class Station(Nameable):
|
||||
class Station(models.Model):
|
||||
"""
|
||||
Represents a radio station, to which multiple programs are attached
|
||||
and that is used as the top object for everything.
|
||||
@ -76,6 +54,8 @@ class Station(Nameable):
|
||||
Theses are set up when needed (at the first access to these elements)
|
||||
then cached.
|
||||
"""
|
||||
name = models.CharField(_('name'), max_length=64)
|
||||
slug = models.SlugField(_('slug'), max_length=64, unique=True)
|
||||
path = models.CharField(
|
||||
_('path'),
|
||||
help_text=_('path to the working directory'),
|
||||
@ -199,6 +179,9 @@ class Station(Nameable):
|
||||
logs = logs[:count]
|
||||
return logs
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def save(self, make_sources=True, *args, **kwargs):
|
||||
if not self.path:
|
||||
self.path = os.path.join(
|
||||
@ -223,7 +206,7 @@ class ProgramManager(models.Manager):
|
||||
return qs.filter(station=station, **kwargs)
|
||||
|
||||
|
||||
class Program(Nameable):
|
||||
class Program(models.Model):
|
||||
"""
|
||||
A Program can either be a Streamed or a Scheduled program.
|
||||
|
||||
@ -241,6 +224,8 @@ class Program(Nameable):
|
||||
verbose_name=_('station'),
|
||||
on_delete=models.CASCADE,
|
||||
)
|
||||
name = models.CharField(_('name'), max_length=64)
|
||||
slug = models.SlugField(_('slug'), max_length=64, unique=True)
|
||||
active = models.BooleanField(
|
||||
_('active'),
|
||||
default=True,
|
||||
@ -254,14 +239,10 @@ class Program(Nameable):
|
||||
|
||||
objects = ProgramManager()
|
||||
|
||||
# TODO: use unique slug
|
||||
@property
|
||||
def path(self):
|
||||
"""
|
||||
Return the path to the programs directory
|
||||
"""
|
||||
return os.path.join(settings.AIRCOX_PROGRAMS_DIR,
|
||||
self.slug + '_' + str(self.id))
|
||||
""" Return program's directory path """
|
||||
return os.path.join(settings.AIRCOX_PROGRAMS_DIR, self.slug)
|
||||
|
||||
def ensure_dir(self, subdir=None):
|
||||
"""
|
||||
@ -299,26 +280,8 @@ class Program(Nameable):
|
||||
def __init__(self, *kargs, **kwargs):
|
||||
super().__init__(*kargs, **kwargs)
|
||||
|
||||
if self.name:
|
||||
self.__original_path = self.path
|
||||
|
||||
def save(self, *kargs, **kwargs):
|
||||
super().save(*kargs, **kwargs)
|
||||
|
||||
if hasattr(self, '__original_path') and \
|
||||
self.__original_path != self.path and \
|
||||
os.path.exists(self.__original_path) and \
|
||||
not os.path.exists(self.path):
|
||||
logger.info('program #%s\'s name changed to %s. Change dir name',
|
||||
self.id, self.name)
|
||||
shutil.move(self.__original_path, self.path)
|
||||
|
||||
sounds = Sound.objects.filter(
|
||||
path__startswith=self.__original_path)
|
||||
|
||||
for sound in sounds:
|
||||
sound.path.replace(self.__original_path, self.path)
|
||||
sound.save()
|
||||
if self.slug:
|
||||
self.__initial_path = self.path
|
||||
|
||||
@classmethod
|
||||
def get_from_path(cl, path):
|
||||
@ -346,6 +309,23 @@ class Program(Nameable):
|
||||
def is_show(self):
|
||||
return self.schedule_set.count() != 0
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def save(self, *kargs, **kwargs):
|
||||
super().save(*kargs, **kwargs)
|
||||
|
||||
path_ = getattr(self, '__initial_path', None)
|
||||
if path_ is not None and path_ != self.path and \
|
||||
os.path.exists(path_) and not os.path.exists(self.path):
|
||||
logger.info('program #%s\'s dir changed to %s - update it.',
|
||||
self.id, self.name)
|
||||
|
||||
shutil.move(path_, self.path)
|
||||
Sound.objects.filter(path__startswith=path_) \
|
||||
.update(path=Concat('path', Substr(F('path'), len(path_))))
|
||||
|
||||
|
||||
|
||||
class Stream(models.Model):
|
||||
"""
|
||||
@ -427,15 +407,15 @@ class Schedule(models.Model):
|
||||
_('frequency'),
|
||||
choices=[(int(y), {
|
||||
'ponctual': _('ponctual'),
|
||||
'first': _('first week of the month'),
|
||||
'second': _('second week of the month'),
|
||||
'third': _('third week of the month'),
|
||||
'fourth': _('fourth week of the month'),
|
||||
'last': _('last week of the month'),
|
||||
'first_and_third': _('first and third weeks of the month'),
|
||||
'second_and_fourth': _('second and fourth weeks of the month'),
|
||||
'every': _('every week'),
|
||||
'one_on_two': _('one week on two'),
|
||||
'first': _('1st {day} of the month'),
|
||||
'second': _('2nd {day} of the month'),
|
||||
'third': _('3rd {day} of the month'),
|
||||
'fourth': _('4th {day} of the month'),
|
||||
'last': _('last {day} of the month'),
|
||||
'first_and_third': _('1st and 3rd {day}s of the month'),
|
||||
'second_and_fourth': _('2nd and 4th {day}s of the month'),
|
||||
'every': _('{day}'),
|
||||
'one_on_two': _('one {day} on two'),
|
||||
}[x]) for x, y in Frequency.__members__.items()],
|
||||
)
|
||||
initial = models.ForeignKey(
|
||||
@ -454,11 +434,22 @@ class Schedule(models.Model):
|
||||
|
||||
return pytz.timezone(self.timezone)
|
||||
|
||||
@property
|
||||
def datetime(self):
|
||||
""" Datetime for this schedule (timezone unaware) """
|
||||
import datetime
|
||||
return datetime.datetime.combine(self.date, self.time)
|
||||
@cached_property
|
||||
def start(self):
|
||||
""" Datetime of the start (timezone unaware) """
|
||||
return tz.datetime.combine(self.date, self.time)
|
||||
|
||||
@cached_property
|
||||
def end(self):
|
||||
""" Datetime of the end """
|
||||
return self.start + utils.to_timedelta(self.duration)
|
||||
|
||||
def get_frequency_verbose(self):
|
||||
""" Return frequency formated for display """
|
||||
from django.template.defaultfilters import date
|
||||
return self.get_frequency_display().format(
|
||||
day=date(self.date, 'l')
|
||||
)
|
||||
|
||||
# initial cached data
|
||||
__initial = None
|
||||
@ -630,7 +621,7 @@ class Schedule(models.Model):
|
||||
|
||||
delta = None
|
||||
if self.initial:
|
||||
delta = self.datetime - self.initial.datetime
|
||||
delta = self.start - self.initial.start
|
||||
|
||||
# FIXME: daylight saving bug: delta misses an hour when diffusion and
|
||||
# rerun are not on the same daylight-saving timezone
|
||||
@ -916,9 +907,12 @@ class Diffusion(models.Model):
|
||||
self.check_conflicts()
|
||||
|
||||
def __str__(self):
|
||||
return '{self.program.name} {date} #{self.pk}'.format(
|
||||
self=self, date=self.local_start.strftime('%Y/%m/%d %H:%M%z')
|
||||
str_ = '{self.program.name} {date}'.format(
|
||||
self=self, date=self.local_start.strftime('%Y/%m/%d %H:%M%z'),
|
||||
)
|
||||
if self.initial:
|
||||
str_ += ' ({})'.format(_('rerun'))
|
||||
return str_
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('Diffusion')
|
||||
@ -928,7 +922,7 @@ class Diffusion(models.Model):
|
||||
)
|
||||
|
||||
|
||||
class Sound(Nameable):
|
||||
class Sound(models.Model):
|
||||
"""
|
||||
A Sound is the representation of a sound file that can be either an excerpt
|
||||
or a complete archive of the related diffusion.
|
||||
@ -939,6 +933,7 @@ class Sound(Nameable):
|
||||
excerpt = 0x02,
|
||||
removed = 0x03,
|
||||
|
||||
name = models.CharField(_('name'), max_length=64)
|
||||
program = models.ForeignKey(
|
||||
Program,
|
||||
verbose_name=_('program'),
|
||||
|
Reference in New Issue
Block a user