documentation

This commit is contained in:
bkfox 2015-11-24 13:44:38 +01:00
parent 5109392db7
commit 49c4939708
7 changed files with 68 additions and 48 deletions

View File

@ -312,10 +312,10 @@ class Dealer (Source):
source.skip() source.skip()
self.controller.log( self.controller.log(
source = self.id, source = self.id,
diffusion = diff,
date = now, date = now,
comment = 'trigger the scheduled diffusion to liquidsoap; ' comment = 'trigger the scheduled diffusion to liquidsoap; '
'skip all other streams', 'skip all other streams',
related_object = diff,
) )
@ -401,9 +401,9 @@ class Controller:
self.log( self.log(
source = source.id, source = source.id,
sound = models.Sound.objects.get(path = on_air),
date = tz.make_aware(tz.datetime.now()), date = tz.make_aware(tz.datetime.now()),
comment = 'sound has changed' comment = 'sound has changed',
related_object = models.Sound.objects.get(path = on_air),
) )
def monitor (self): def monitor (self):

View File

@ -1,22 +1,30 @@
This application defines all base models and basic control of them. We have: This application defines all base models and basic control of them. We have:
* **Nameable**: generic class used in any class needing to be named. Includes some utility functions; * **Nameable**: generic class used in any class needing to be named. Includes some utility functions;
* **Program**: the program itself; * **Program**: the program itself;
* **Episode**: occurence of a program; * **Diffusion**: occurrence of a program planified in the timetable. For rerun, informations are bound to the initial diffusion;
* **Diffusion**: diffusion of an episode in the timetable, linked to an episode (an episode can have multiple diffusions);
* **Schedule**: describes diffusions frequencies for each program; * **Schedule**: describes diffusions frequencies for each program;
* **Track**: track informations in a playlist of an episode; * **Track**: track informations in a playlist of a diffusion;
* **Sound**: information about a sound that can be used for podcast or rerun; * **Sound**: information about a sound that can be used for podcast or rerun;
* **Log**: logs
# Program
Each program has a directory in **AIRCOX_PROGRAMS_DATA**; For each, subdir: # Architecture
A Station is basically an object that represent a radio station. On each station, we use the Program object, that is declined in two different type:
* **Scheduled**: the diffusion is based on a timetable and planified through one Schedule or more; Diffusion object represent the occurrence of these programs;
* **Streamed**: the diffusion is based on random playlist, used to fill gaps between the programs;
Each program has a directory in **AIRCOX_PROGRAMS_DIR**; For each, subdir:
* **archives**: complete episode record, can be used for diffusions or as a podcast * **archives**: complete episode record, can be used for diffusions or as a podcast
* **excerpts**: excerpt of an episode, or other elements, can be used as a podcast * **excerpts**: excerpt of an episode, or other elements, can be used as a podcast
Each program has a schedule, defined through multiple schedule elements. This schedule can calculate the next dates of diffusion, if is a rerun (of wich diffusion), etc.
Basically, for each program created, we can define some options, a directory in **AIRCOX_PROGRAMS_DATA**, where subfolders defines some informations about a file. # manage.py's commands
* **diffusions_monitor**: update/create, check and clean diffusions; When a diffusion is created, its type is unconfirmed, and requires a manual approval to be on the timetable.
* **sound_monitor**: check for existing and missing sounds files in programs directories and synchronize the database. Can also check for the quality of file and synchronize the database according to them.
* **sound_quality_check**: check for the quality of the file (don't update database)
# Notes # External Requirements
We don't give any view on what should be now, because it is up to the stream generator to give info about what is running. * Sox (and soxi): sound file monitor and quality check
* Requirements.txt for python's dependecies

View File

@ -0,0 +1,2 @@
django-taggit >= 0.17.1

View File

@ -4,7 +4,6 @@ from django import forms
from django.contrib import admin from django.contrib import admin
from django.db import models from django.db import models
from suit.admin import SortableTabularInline, SortableModelAdmin
from aircox_programs.forms import * from aircox_programs.forms import *
from aircox_programs.models import * from aircox_programs.models import *
@ -13,7 +12,6 @@ from aircox_programs.models import *
# #
# Inlines # Inlines
# #
# TODO: inherits from the corresponding admin view
class SoundInline (admin.TabularInline): class SoundInline (admin.TabularInline):
model = Sound model = Sound
@ -28,12 +26,13 @@ class StreamInline (admin.TabularInline):
extra = 1 extra = 1
class TrackInline (SortableTabularInline): # from suit.admin import SortableTabularInline, SortableModelAdmin
fields = ['artist', 'name', 'tags', 'position'] #class TrackInline (SortableTabularInline):
form = TrackForm # fields = ['artist', 'name', 'tags', 'position']
model = Track # form = TrackForm
sortable = 'position' # model = Track
extra = 10 # sortable = 'position'
# extra = 10
class NameableAdmin (admin.ModelAdmin): class NameableAdmin (admin.ModelAdmin):
@ -86,22 +85,20 @@ class DiffusionAdmin (admin.ModelAdmin):
sounds = [ str(s) for s in obj.get_archives()] sounds = [ str(s) for s in obj.get_archives()]
return ', '.join(sounds) if sounds else '' return ', '.join(sounds) if sounds else ''
list_display = ('id', 'type', 'date', 'archives', 'program', 'initial') list_display = ('id', 'type', 'date', 'program', 'initial', 'archives')
list_filter = ('type', 'date', 'program') list_filter = ('type', 'date', 'program')
list_editable = ('type', 'date') list_editable = ('type', 'date')
fields = ['type', 'date', 'initial', 'sounds', 'program'] fields = ['type', 'date', 'initial', 'program', 'sounds']
readonly_fields = ('duration',)
def get_form(self, request, obj=None, **kwargs): def get_form(self, request, obj=None, **kwargs):
if obj: if request.user.has_perm('aircox_program.programming'):
if obj.date < tz.make_aware(tz.datetime.now()): self.readonly_fields = []
self.readonly_fields = list(self.fields) else:
self.readonly_fields.remove('type') self.readonly_fields = ['program', 'date', 'duration']
elif obj.initial:
self.readonly_fields = ['program', 'sounds'] if obj.initial:
else: self.readonly_fields += ['program', 'sounds']
self.readonly_fields = []
return super().get_form(request, obj, **kwargs) return super().get_form(request, obj, **kwargs)
def get_queryset(self, request): def get_queryset(self, request):
@ -113,7 +110,11 @@ class DiffusionAdmin (admin.ModelAdmin):
return qs.exclude(type = Diffusion.Type['unconfirmed']) return qs.exclude(type = Diffusion.Type['unconfirmed'])
admin.site.register(Log) @admin.register(Log)
class LogAdmin (admin.ModelAdmin):
list_display = ['id', 'date', 'source', 'comment', 'related_object']
list_filter = ['date', 'related_type']
admin.site.register(Track) admin.site.register(Track)
admin.site.register(Schedule) admin.site.register(Schedule)

View File

@ -5,6 +5,8 @@ from django.template.defaultfilters import slugify
from django.utils.translation import ugettext as _, ugettext_lazy from django.utils.translation import ugettext as _, ugettext_lazy
from django.utils import timezone as tz from django.utils import timezone as tz
from django.utils.html import strip_tags from django.utils.html import strip_tags
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from taggit.managers import TaggableManager from taggit.managers import TaggableManager
@ -33,9 +35,9 @@ class Nameable (models.Model):
@property @property
def slug (self): def slug (self):
return self.get_slug_name() """
Slug based on the name. We replace '-' by '_'
def get_slug_name (self): """
return slugify(self.name).replace('-', '_') return slugify(self.name).replace('-', '_')
def __str__ (self): def __str__ (self):
@ -450,8 +452,11 @@ class Program (Nameable):
@property @property
def path (self): def path (self):
"""
Return the path to the programs directory
"""
return os.path.join(settings.AIRCOX_PROGRAMS_DIR, return os.path.join(settings.AIRCOX_PROGRAMS_DIR,
self.get_slug_name() + '_' + str(self.id) ) self.slug + '_' + str(self.id) )
def ensure_dir (self, subdir = None): def ensure_dir (self, subdir = None):
""" """
@ -498,10 +503,9 @@ class Diffusion (models.Model):
- stop: the diffusion has been manually stopped - stop: the diffusion has been manually stopped
""" """
Type = { Type = {
'default': 0x00, # confirmed diffusion case FIXME 'default': 0x00, # diffusion is planified
'unconfirmed': 0x01, # scheduled by the generator but not confirmed for diffusion 'unconfirmed': 0x01, # scheduled by the generator but not confirmed for diffusion
'cancel': 0x02, # cancellation happened; used to inform users 'cancel': 0x02, # diffusion canceled
# 'restart': 0x03, # manual restart; used to remix/give up antenna
} }
for key, value in Type.items(): for key, value in Type.items():
ugettext_lazy(key) ugettext_lazy(key)
@ -592,6 +596,10 @@ class Diffusion (models.Model):
verbose_name = _('Diffusion') verbose_name = _('Diffusion')
verbose_name_plural = _('Diffusions') verbose_name_plural = _('Diffusions')
permissions = (
('programming', _('edit the diffusion\'s planification')),
)
class Log (models.Model): class Log (models.Model):
""" """
@ -603,11 +611,6 @@ class Log (models.Model):
help_text = 'source information', help_text = 'source information',
blank = True, null = True, blank = True, null = True,
) )
diffusion = models.ForeignKey(
'Diffusion',
help_text = _('related diffusion'),
blank = True, null = True,
)
sound = models.ForeignKey( sound = models.ForeignKey(
'Sound', 'Sound',
help_text = _('played sound'), help_text = _('played sound'),
@ -620,6 +623,16 @@ class Log (models.Model):
max_length = 512, max_length = 512,
blank = True, null = True, blank = True, null = True,
) )
related_type = models.ForeignKey(
ContentType,
blank = True, null = True,
)
related_id = models.PositiveIntegerField(
blank = True, null = True,
)
related_object = GenericForeignKey(
'related_type', 'related_id',
)
def print (self): def print (self):
print(str(self), ':', self.comment or '') print(str(self), ':', self.comment or '')

View File

@ -33,5 +33,3 @@ ensure('AIRCOX_SOUND_FILE_EXT',
# Stream for the scheduled diffusions # Stream for the scheduled diffusions
ensure('AIRCOX_SCHEDULED_STREAM', 0) ensure('AIRCOX_SCHEDULED_STREAM', 0)

View File

@ -8,8 +8,6 @@ class Program (RelatedPost):
model = programs.Program model = programs.Program
bind_mapping = True bind_mapping = True
mapping = { mapping = {
'title': 'name',
'content': 'description',
} }
class Episode (RelatedPost): class Episode (RelatedPost):