Merge branch 'streamer_live'

This commit is contained in:
bkfox 2017-06-30 14:18:56 +02:00
commit 013a0894ab
9 changed files with 243 additions and 235 deletions

View File

@ -183,8 +183,8 @@ class StationAdmin(admin.ModelAdmin):
@admin.register(Log) @admin.register(Log)
class LogAdmin(admin.ModelAdmin): class LogAdmin(admin.ModelAdmin):
list_display = ['id', 'date', 'station', 'source', 'type', 'comment', 'related'] list_display = ['id', 'date', 'station', 'source', 'type', 'comment', 'diffusion', 'sound', 'track']
list_filter = ['date', 'source', 'related_type'] list_filter = ['date', 'source', 'diffusion', 'sound', 'track']
admin.site.register(Port) admin.site.register(Port)

View File

@ -15,7 +15,14 @@ from django.conf import settings as main_settings
from django.core.management.base import BaseCommand, CommandError from django.core.management.base import BaseCommand, CommandError
from django.utils import timezone as tz from django.utils import timezone as tz
from aircox.models import Station, Diffusion, Track, Sound, Log from aircox.models import Station, Diffusion, Track, Sound, Log #, DiffusionLog, SoundLog
class Tracer:
"""
Keep trace of played item and update logs in adequation to it
"""
pass
class Monitor: class Monitor:
@ -65,11 +72,12 @@ class Monitor:
self.sync_playlists() self.sync_playlists()
self.handle() self.handle()
def log(self, **kwargs): def log(self, date = None, **kwargs):
""" """
Create a log using **kwargs, and print info Create a log using **kwargs, and print info
""" """
log = Log(station = self.station, **kwargs) log = Log(station = self.station, date = date or tz.now(),
**kwargs)
log.save() log.save()
log.print() log.print()
@ -84,17 +92,18 @@ class Monitor:
if not current_sound or not current_source: if not current_sound or not current_source:
return return
log = Log.objects.get_for(self.station, model = Sound) \ log = Log.objects.station(self.station, sound__isnull = False) \
.select_related('sound') \
.order_by('date').last() .order_by('date').last()
# only streamed # only streamed ns
if log and (log.related and not log.related.diffusion): if log and not log.sound.diffusion:
self.trace_sound_tracks(log) self.trace_sound_tracks(log)
# TODO: expiration # TODO: expiration
if log and (log.source == current_source.id and \ if log and (log.source == current_source.id and \
log.related and log.sound and
log.related.path == current_sound): log.sound.path == current_sound):
return return
sound = Sound.objects.filter(path = current_sound) sound = Sound.objects.filter(path = current_sound)
@ -102,7 +111,7 @@ class Monitor:
type = Log.Type.play, type = Log.Type.play,
source = current_source.id, source = current_source.id,
date = tz.now(), date = tz.now(),
related = sound[0] if sound else None, sound = sound[0] if sound else None,
# keep sound path (if sound is removed, we keep that info) # keep sound path (if sound is removed, we keep that info)
comment = current_sound, comment = current_sound,
) )
@ -112,11 +121,12 @@ class Monitor:
Log tracks for the given sound (for streamed programs); Called by Log tracks for the given sound (for streamed programs); Called by
self.trace self.trace
""" """
logs = Log.objects.get_for(self.station, model = Track) \ logs = Log.objects.station(self.station,
.filter(pk__gt = log.pk) track__isnull = False,
logs = [ log.related_id for log in logs ] pk__gt = log.pk) \
.values_list('sound__pk', flat = True)
tracks = Track.objects.get_for(object = log.related) \ tracks = Track.objects.get_for(object = log.sound) \
.filter(in_seconds = True) .filter(in_seconds = True)
if tracks and len(tracks) == len(logs): if tracks and len(tracks) == len(logs):
return return
@ -130,7 +140,7 @@ class Monitor:
type = Log.Type.play, type = Log.Type.play,
source = log.source, source = log.source,
date = pos, date = pos,
related = track, track = track,
comment = track, comment = track,
) )
@ -147,9 +157,9 @@ class Monitor:
for source in self.station.sources: for source in self.station.sources:
if source == self.station.dealer: if source == self.station.dealer:
continue continue
playlist = [ sound.path for sound in playlist = source.program.sound_set.all() \
source.program.sound_set.all() ] .values_list('path', flat = True)
source.playlist = playlist source.playlist = list(playlist)
def trace_canceled(self): def trace_canceled(self):
""" """
@ -163,18 +173,18 @@ class Monitor:
type = Diffusion.Type.normal, type = Diffusion.Type.normal,
sound__type = Sound.Type.archive, sound__type = Sound.Type.archive,
) )
logs = station.played(models = Diffusion) logs = station.played(diffusion__isnull = False)
date = tz.now() - datetime.timedelta(seconds = self.cancel_timeout) date = tz.now() - datetime.timedelta(seconds = self.cancel_timeout)
for diff in diffs: for diff in diffs:
if logs.filter(related = diff): if logs.filter(diffusion = diff):
continue continue
if diff.start < now: if diff.start < now:
diff.type = Diffusion.Type.canceled diff.type = Diffusion.Type.canceled
diff.save() diff.save()
self.log( self.log(
type = Log.Type.other, type = Log.Type.other,
related = diff, diffusion = diff,
comment = 'Diffusion canceled after {} seconds' \ comment = 'Diffusion canceled after {} seconds' \
.format(self.cancel_timeout) .format(self.cancel_timeout)
) )
@ -187,53 +197,81 @@ class Monitor:
station = self.station station = self.station
now = tz.now() now = tz.now()
diff_log = station.played(models = Diffusion) \ log = station.played(diffusion__isnull = False) \
.select_related('diffusion') \
.order_by('date').last() .order_by('date').last()
if not diff_log or \ if not log or not log.diffusion.is_date_in_range(now):
not diff_log.related.is_date_in_range(now): # not running anymore
return None, [] return None, []
# sound has switched? assume it has been (forced to) stopped # last sound source change: end of file reached or forced to stop
sounds = station.played(models = Sound) \ sounds = station.played(sound__isnull = False) \
.filter(date__gte = diff_log.date) \ .filter(date__gte = log.date) \
.order_by('date') .order_by('date')
if sounds.last() and sounds.last().source != diff_log.source: if sounds.count() and sounds.last().source != log.source:
return diff_log, [] return None, []
# last diff is still playing: get the remaining playlist # last diff is still playing: get remaining playlist
sounds = sounds.filter( sounds = sounds \
source = diff_log.source, pk__gt = diff_log.pk .filter(source = log.source, pk__gt = log.pk) \
) .exclude(sound__type = Sound.Type.removed)
sounds = [
sound.related.path for sound in sounds
if sound.related.type != Sound.Type.removed
]
return ( remaining = log.diffusion.get_archives().exclude(pk__in = sounds) \
diff_log.related, .values_list('path', flat = True)
[ path for path in diff_log.related.playlist return log.diffusion, list(remaining)
if path not in sounds ]
)
def __next_diff(self, diff): def __next_diff(self, diff):
""" """
Return the tuple with the next diff that should be played and Return the next diffusion to be played as tuple of (diff, playlist).
the playlist If diff is given, it is the one to be played right after it.
Note: diff is a log
""" """
station = self.station station = self.station
now = tz.now() now = tz.now()
args = {'start__gt': diff.date } if diff else {} kwargs = {'start__gte': diff.end } if diff else {}
diff = Diffusion.objects.at(station, now).filter( diff = Diffusion.objects \
type = Diffusion.Type.normal, .at(station, now) \
sound__type = Sound.Type.archive, .filter(type = Diffusion.Type.normal, **kwargs) \
**args .distinct().order_by('start')
).distinct().order_by('start').first() diff = diff.first()
return (diff, diff and diff.playlist or []) return (diff, diff and diff.playlist or [])
def handle_pl_sync(self, source, playlist, diff = None, date = None):
"""
Update playlist of a source if required, and handle logging when
it is needed.
"""
dealer = self.station.dealer
if dealer.playlist != playlist:
dealer.playlist = playlist
if diff and not diff.is_live():
self.log(type = Log.Type.load, source = source.id,
diffusion = diff, date = date)
def handle_diff_start(self, source, diff, date):
"""
Enable dealer in order to play a given diffusion if required,
handle start of diffusion
"""
if not diff or diff.start > date:
return
# live: just log it
if diff.is_live():
diff_ = Log.objects.station(self.station) \
.filter(diffusion = diff)
if not diff_.count():
self.log(type = Log.Type.on_air, source = source.id,
diffusion = diff, date = date)
return
# enable dealer
if not dealer.active:
dealer.active = True
self.log(type = Log.Type.play, source = source.id,
diffusion = diff, date = date)
def handle(self): def handle(self):
""" """
Handle scheduled diffusion, trigger if needed, preload playlists Handle scheduled diffusion, trigger if needed, preload playlists
@ -246,33 +284,15 @@ class Monitor:
now = tz.now() now = tz.now()
# current and next diffs # current and next diffs
diff, playlist = self.__current_diff() current_diff, remaining_pl = self.__current_diff()
dealer.active = bool(playlist) next_diff, next_pl = self.__next_diff(current_diff)
next_diff, next_playlist = self.__next_diff(diff) # playlist
playlist += next_playlist dealer.active = bool(remaining_pl)
playlist = remaining_pl + next_pl
# playlist update self.handle_pl_sync(dealer, playlist, next_diff, now)
if dealer.playlist != playlist: self.handle_diff_start(dealer, next_diff, now)
dealer.playlist = playlist
if next_diff:
self.log(
type = Log.Type.load,
source = dealer.id,
date = now,
related = next_diff
)
# dealer.on when next_diff start <= now
if next_diff and not dealer.active and \
next_diff.start <= now:
dealer.active = True
self.log(
type = Log.Type.play,
source = dealer.id,
date = now,
related = next_diff,
)
class Command (BaseCommand): class Command (BaseCommand):

View File

@ -50,7 +50,6 @@ class RelatedManager(models.Manager):
qs = qs.filter(related_id = object.pk) qs = qs.filter(related_id = object.pk)
return qs return qs
class Related(models.Model): class Related(models.Model):
""" """
Add a field "related" of type GenericForeignKey, plus utilities. Add a field "related" of type GenericForeignKey, plus utilities.
@ -148,7 +147,6 @@ class Track(Related):
verbose_name = _('Track') verbose_name = _('Track')
verbose_name_plural = _('Tracks') verbose_name_plural = _('Tracks')
# #
# Station related classes # Station related classes
# #
@ -180,7 +178,7 @@ class Station(Nameable):
__dealer = None __dealer = None
__streamer = None __streamer = None
def __prepare(self): def __prepare_controls(self):
import aircox.controllers as controllers import aircox.controllers as controllers
if not self.__streamer: if not self.__streamer:
self.__streamer = controllers.Streamer(station = self) self.__streamer = controllers.Streamer(station = self)
@ -215,12 +213,12 @@ class Station(Nameable):
""" """
Audio sources, dealer included Audio sources, dealer included
""" """
self.__prepare() self.__prepare_controls()
return self.__sources return self.__sources
@property @property
def dealer(self): def dealer(self):
self.__prepare() self.__prepare_controls()
return self.__dealer return self.__dealer
@property @property
@ -228,86 +226,25 @@ class Station(Nameable):
""" """
Audio controller for the station Audio controller for the station
""" """
self.__prepare() self.__prepare_controls()
return self.__streamer return self.__streamer
def played(self, models, archives = True): def played(self, *args, **kwargs):
""" """
Call Log.objects.played for this station Call Log.objects.played for this station
""" """
return Log.objects.played(self, models, archives) return Log.objects.played(self, *args, **kwargs)
@staticmethod
def __mix_logs_and_diff(diffs, logs, count = 0):
"""
Mix together logs and diffusion items of the same day,
ordered by their date.
Diffs and Logs are assumed to be ordered by -date, and so is
the resulting list
"""
# we fill a list with diff and retrieve logs that happened between
# each to put them too there.
# we do the algorithm in the reverse way in order to be able to limit
# process calculations using count if needed.
diff_ = None
now = tz.now()
items = []
logs = logs.order_by('-date')
for diff in diffs.order_by('-start'):
if diff_:
logs_ = logs.filter(date__gt = diff.end, date__lt = diff_.start)
else:
logs_ = logs.filter(date__gt = diff.end)
if diff.end < now:
# a log can be started before the end of the diffusion and still
# is running. We can't say if it has been properly finished
# before the end of the diffusion, but we assume that in most
# cases this is true.
# We just check if there is some other log after this partial
# one.
partial = logs.filter(
date__gt = diff.start, date__lt = diff.end
).last()
if partial:
next_log = logs.filter(pk__gt = partial.pk).first()
if not next_log or next_log.date > diff.end:
partial.date = diff.end
logs_ = list(logs_) + [partial]
# append to list
diff_ = diff
items.extend(logs_)
items.append(diff)
if count and len(items) >= count:
break
if diff_:
if count and len(items) >= count:
return items[:count]
logs_ = logs.filter(date__lt = diff_.start)
else:
logs_ = logs.all()
items.extend(logs_)
return items[:count] if count else items
def on_air(self, date = None, count = 0): def on_air(self, date = None, count = 0):
""" """
Return a list of what happened on air, based on logs and Return a queryset of what happened on air, based on logs and
diffusions informations. The list is sorted by -date. diffusions informations. The queryset is sorted by -date.
* date: only for what happened on this date; * date: only for what happened on this date;
* count: number of items to retrieve if not zero; * count: number of items to retrieve if not zero;
If date is not specified, count MUST be set to a non-zero value. If date is not specified, count MUST be set to a non-zero value.
Be careful with what you which for: the result is a plain list. Be careful with what you which for: the result is a plain list.
The list contains:
* track logs: for the streamed programs;
* diffusion: for the scheduled diffusions;
""" """
# FIXME: as an iterator? # FIXME: as an iterator?
# TODO argument to get sound instead of tracks # TODO argument to get sound instead of tracks
@ -315,21 +252,39 @@ class Station(Nameable):
raise ValueError('at least one argument must be set') raise ValueError('at least one argument must be set')
# FIXME can be a potential source of bug # FIXME can be a potential source of bug
if date:
date = utils.cast_date(date, to_datetime = False)
if date and date > datetime.date.today(): if date and date > datetime.date.today():
return [] return []
if date: if date:
logs = Log.objects.at_for(self, date, model = Track) logs = Log.objects.at(self, date)
diffs = Diffusion.objects.at(self, date) diffs = Diffusion.objects.at(self, date, type = Diffusion.Type.normal) \
.order_by('-start')
else: else:
logs = Log.objects.get_for(self, model = Track) logs = Log.objects
diffs = Diffusion.objects diffs = Diffusion.objects.filter(type = Diffusion.Type.normal,
logs = logs.filter(station = self) start__lte = tz.now()) \
.order_by('-start')[:count]
diffs = diffs.filter(program__station = self) \ q = models.Q(diffusion__isnull = False) | \
.filter(type = Diffusion.Type.normal) \ models.Q(track__isnull = False)
.filter(start__lte = tz.now()) logs = logs.filter(q).order_by('-date')
return self.__mix_logs_and_diff(diffs, logs, count)
# filter out tracks played when there was a diffusion
n = 0
q = models.Q()
for diff in diffs:
if count and n >= count:
break
q = q | models.Q(date__gte = diff.start, date__lte = diff.end)
n += 1
logs = logs.exclude(q, diffusion__isnull = True)
if count:
logs = logs[:count]
return logs
def save(self, make_sources = True, *args, **kwargs): def save(self, make_sources = True, *args, **kwargs):
if not self.path: if not self.path:
@ -348,9 +303,9 @@ class Station(Nameable):
class ProgramManager(models.Manager): class ProgramManager(models.Manager):
def station(self, station, qs = None): def station(self, station, qs = None, **kwargs):
qs = self if qs is None else qs qs = self if qs is None else qs
return qs.filter(station = station) return qs.filter(station = station, **kwargs)
class Program(Nameable): class Program(Nameable):
""" """
@ -730,11 +685,11 @@ class Schedule(models.Model):
class DiffusionManager(models.Manager): class DiffusionManager(models.Manager):
def station(self, station, qs = None): def station(self, station, qs = None, **kwargs):
qs = self if qs is None else qs qs = self if qs is None else qs
return qs.filter(program__station = station) return qs.filter(program__station = station, **kwargs)
def at(self, station, date = None, next = False, qs = None): def at(self, station, date = None, next = False, qs = None, **kwargs):
""" """
Return diffusions occuring at the given date, ordered by +start Return diffusions occuring at the given date, ordered by +start
@ -770,7 +725,7 @@ class DiffusionManager(models.Manager):
if next: if next:
# include also diffusions of the next day # include also diffusions of the next day
filters |= models.Q(start__gte = start) filters |= models.Q(start__gte = start)
qs = qs.filter(filters) qs = qs.filter(filters, **kwargs)
return self.station(station, qs).order_by('start').distinct() return self.station(station, qs).order_by('start').distinct()
def after(self, station, date = None, qs = None): def after(self, station, date = None, qs = None):
@ -867,7 +822,12 @@ class Diffusion(models.Model):
""" """
List of archives' path; uses get_archives List of archives' path; uses get_archives
""" """
return [ sound.path for sound in self.get_archives() ] playlist = self.get_archives().values_list('path', flat = True)
return list(playlist)
def is_live(self):
return self.type == self.Type.normal and \
not self.get_archives().count()
def get_archives(self): def get_archives(self):
""" """
@ -1229,59 +1189,51 @@ class Port (models.Model):
) )
class LogManager(RelatedManager): class LogManager(models.Manager):
def station(self, station, qs = None): def station(self, station, qs = None, **kwargs):
qs = self if qs is None else qs qs = self if qs is None else qs
return qs.filter(station = station) return qs.filter(station = station, **kwargs)
def get_for(self, station, *args, **kwargs): def _at(self, date = None, qs = None, **kwargs):
qs = super().get_for(*args, **kwargs)
return self.station(station, qs) if station else qs
def _at(self, date = None, qs = None):
start, end = utils.date_range(date) start, end = utils.date_range(date)
qs = self if qs is None else qs qs = self if qs is None else qs
return qs.filter(date__gte = start, return qs.filter(date__gte = start, date__lte = end, **kwargs)
date__lte = end)
def at(self, station = None, date = None, qs = None): def at(self, station = None, date = None, qs = None, **kwargs):
""" """
Return a queryset of logs that have the given date Return a queryset of logs that have the given date
in their range. in their range.
""" """
qs = self._at(date, qs) qs = self._at(date, qs, **kwargs)
return self.station(station, qs) if station else qs return self.station(station, qs) if station else qs
def at_for(self, station, date, object = None, model = None, qs = None): def played(self, station, archives = True, include_live = True,
""" **kwargs):
Return a queryset of logs that occured at the given date
for the given model or object.
"""
qs = self.get_for(station, object, model, qs)
return self._at(date, qs)
def played(self, station, models, archives = True):
""" """
Return a queryset of the played elements' log for the given Return a queryset of the played elements' log for the given
station and model. This queryset is ordered by date ascending station and model. This queryset is ordered by date ascending
* station: related station * station: related station
* models: a model or a list of models
* archives: if false, exclude log of diffusion's archives from * archives: if false, exclude log of diffusion's archives from
the queryset; the queryset;
* include_live: include diffusion that have no archive
* kwargs: extra filter kwargs
""" """
qs = self.get_for(station, model = models) \ if include_live:
.filter(type = Log.Type.play) qs = self.filter(type__in = (Log.Type.play, Log.Type.on_air),
**kwargs)
else:
qs = self.filter(type = Log.Type.play, **kwargs)
if not archives and station.dealer: if not archives and station.dealer:
qs = qs.exclude( qs = qs.exclude(
source = station.dealer.id, source = station.dealer.id,
related_type = ContentType.objects.get_for_model(Sound) sound__isnull = False
) )
return qs.order_by('date') return qs.order_by('date')
class Log(Related): class Log(models.Model):
""" """
Log sounds and diffusions that are played on the station. Log sounds and diffusions that are played on the station.
@ -1303,14 +1255,18 @@ class Log(Related):
""" """
Source starts to be preload related_object Source starts to be preload related_object
""" """
other = 0x03 on_air = 0x03
"""
A diffusion occured, but in live (no sound played by Aircox)
"""
other = 0x04
""" """
Other log Other log
""" """
type = models.SmallIntegerField( type = models.SmallIntegerField(
verbose_name = _('type'), verbose_name = _('type'),
choices = [ (int(y), _(x)) for x,y in Type.__members__.items() ], choices = [ (int(y), _(x.replace('_',' '))) for x,y in Type.__members__.items() ],
blank = True, null = True, blank = True, null = True,
) )
station = models.ForeignKey( station = models.ForeignKey(
@ -1329,6 +1285,7 @@ class Log(Related):
date = models.DateTimeField( date = models.DateTimeField(
_('date'), _('date'),
default=tz.now, default=tz.now,
db_index = True,
) )
comment = models.CharField( comment = models.CharField(
_('comment'), _('comment'),
@ -1336,6 +1293,25 @@ class Log(Related):
blank = True, null = True, blank = True, null = True,
) )
diffusion = models.ForeignKey(
Diffusion,
verbose_name = _('Diffusion'),
blank = True, null = True,
db_index = True,
)
sound = models.ForeignKey(
Sound,
verbose_name = _('Sound'),
blank = True, null = True,
db_index = True,
)
track = models.ForeignKey(
Track,
verbose_name = _('Track'),
blank = True, null = True,
db_index = True,
)
objects = LogManager() objects = LogManager()
@property @property
@ -1343,12 +1319,16 @@ class Log(Related):
""" """
Calculated end using self.related informations Calculated end using self.related informations
""" """
if self.related_type == Diffusion: if self.diffusion:
return self.related.end return self.diffusion.end
if self.related_type == Sound: if self.sound:
return self.date + to_timedelta(self.duration) return self.date + to_timedelta(sound.duration)
return self.date return self.date
@property
def related(self):
return self.diffusion or self.sound or self.track
def is_expired(self, date = None): def is_expired(self, date = None):
""" """
Return True if the log is expired. Note that it only check Return True if the log is expired. Note that it only check
@ -1359,11 +1339,19 @@ class Log(Related):
return self.end < date return self.end < date
def print(self): def print(self):
r = []
if self.diffusion:
r.append('diff: ' + str(self.diffusion_id))
if self.sound:
r.append('sound: ' + str(self.sound_id))
if self.track:
r.append('track: ' + str(self.track_id))
logger.info('log #%s: %s%s', logger.info('log #%s: %s%s',
str(self), str(self),
self.comment or '', self.comment or '',
' -- {} #{}'.format(self.related_type, self.related_id) ' (' + ', '.join(r) + ')' if r else ''
if self.related else ''
) )
def __str__(self): def __str__(self):
@ -1371,4 +1359,3 @@ class Log(Related):
self.pk, self.date.strftime('%Y/%m/%d %H:%M'), self.source self.pk, self.date.strftime('%Y/%m/%d %H:%M'), self.source
) )

View File

@ -40,12 +40,12 @@ def on_air(request):
else: else:
station = stations.stations.first() station = stations.stations.first()
last = station.on_air(count = 10) on_air = station.on_air(count = 10).select_related('track','diffusion')
if not last: if not on_air.count():
return HttpResponse('') return HttpResponse('')
last = last[0] last = on_air.last()
if type(last) == models.Log: if last.track:
last = { last = {
'type': 'track', 'type': 'track',
'artist': last.related.artist, 'artist': last.related.artist,
@ -54,11 +54,12 @@ def on_air(request):
} }
else: else:
try: try:
diff = last.diffusion
publication = None publication = None
if cms: if cms:
publication = \ publication = \
cms.DiffusionPage.objects.filter( cms.DiffusionPage.objects.filter(
diffusion = last.initial or last).first() or \ diffusion = diff.initial or diff).first() or \
cms.ProgramPage.objects.filter( cms.ProgramPage.objects.filter(
program = last.program).first() program = last.program).first()
except: except:
@ -66,8 +67,8 @@ def on_air(request):
last = { last = {
'type': 'diffusion', 'type': 'diffusion',
'title': last.program.name, 'title': diff.program.name,
'date': last.start, 'date': diff.start,
'url': publication.specific.url if publication else None, 'url': publication.specific.url if publication else None,
} }

View File

@ -64,7 +64,7 @@ class Command (BaseCommand):
initial__isnull = True initial__isnull = True
).exclude(type = Diffusion.Type.unconfirmed) ).exclude(type = Diffusion.Type.unconfirmed)
for diffusion in qs: for diffusion in qs:
if not diffusion.program.page.count(): if not diffusion.program.page:
if not hasattr(diffusion.program, '__logged_diff_error'): if not hasattr(diffusion.program, '__logged_diff_error'):
logger.warning( logger.warning(
'the program {} has no page; skip the creation of ' 'the program {} has no page; skip the creation of '
@ -80,7 +80,7 @@ class Command (BaseCommand):
page = DiffusionPage.from_diffusion( page = DiffusionPage.from_diffusion(
diffusion, live = False diffusion, live = False
) )
diffusion.program.page.first().add_child(instance = page) diffusion.program.page.add_child(instance = page)
except: except:
import sys import sys
e = sys.exc_info()[0] e = sys.exc_info()[0]

View File

@ -452,10 +452,8 @@ class ProgramPage(Publication):
def diffs_to_page(self, diffs): def diffs_to_page(self, diffs):
for diff in diffs: for diff in diffs:
if diff.page.count(): if not diff.page:
diff.page_ = diff.page.first() diff.page = ListItem(
else:
diff.page_ = ListItem(
title = '{}, {}'.format( title = '{}, {}'.format(
self.program.name, diff.date.strftime('%d %B %Y') self.program.name, diff.date.strftime('%d %B %Y')
), ),
@ -464,7 +462,7 @@ class ProgramPage(Publication):
date = diff.start, date = diff.start,
) )
return [ return [
diff.page_ for diff in diffs if diff.page_.live diff.page for diff in diffs if diff.page.live
] ]
@property @property
@ -560,8 +558,8 @@ class DiffusionPage(Publication):
'title': '{}, {}'.format( 'title': '{}, {}'.format(
diff.program.name, tz.localtime(diff.date).strftime('%d %B %Y') diff.program.name, tz.localtime(diff.date).strftime('%d %B %Y')
), ),
'cover': (diff.program.page.count() and \ 'cover': (diff.program.page and \
diff.program.page.first().cover) or None, diff.program.page.cover) or None,
'date': diff.start, 'date': diff.start,
} }
model_kwargs.update(kwargs) model_kwargs.update(kwargs)
@ -637,7 +635,7 @@ class DiffusionPage(Publication):
if self.diffusion: if self.diffusion:
# set publish_as # set publish_as
if not self.pk: if not self.pk:
self.publish_as = self.diffusion.program.page.first() self.publish_as = self.diffusion.program.page
# sync date # sync date
self.date = self.diffusion.start self.date = self.diffusion.start
@ -777,8 +775,9 @@ class LogsPage(DatedListPage):
logs = [] logs = []
for date in context['nav_dates']['dates']: for date in context['nav_dates']['dates']:
items = [ SectionLogsList.as_item(item) items = self.station.on_air(date = date) \
for item in self.station.on_air(date = date) ] .select_related('track','diffusion')
items = [ SectionLogsList.as_item(item) for item in items ]
logs.append( logs.append(
(date, reversed(items) if self.reverse else items) (date, reversed(items) if self.reverse else items)
) )

View File

@ -957,16 +957,16 @@ class SectionLogsList(SectionItem):
Supports: Log/Track, Diffusion Supports: Log/Track, Diffusion
""" """
from aircox_cms.models import DiffusionPage from aircox_cms.models import DiffusionPage
if type(log) == aircox.models.Diffusion: if log.diffusion:
return DiffusionPage.as_item(log) return DiffusionPage.as_item(log.diffusion)
related = log.related track = log.track
return ListItem( return ListItem(
title = '{artist} -- {title}'.format( title = '{artist} -- {title}'.format(
artist = related.artist, artist = track.artist,
title = related.title, title = track.title,
), ),
headline = related.info, headline = track.info,
date = log.date, date = log.date,
info = '', info = '',
css_class = 'track' css_class = 'track'

View File

@ -118,7 +118,7 @@ def station_post_saved(sender, instance, created, *args, **kwargs):
@receiver(post_save, sender=aircox.Program) @receiver(post_save, sender=aircox.Program)
def program_post_saved(sender, instance, created, *args, **kwargs): def program_post_saved(sender, instance, created, *args, **kwargs):
if not created or instance.page.count(): if not created or instance.page:
return return
settings = utils.get_station_settings(instance.station) settings = utils.get_station_settings(instance.station)
@ -191,7 +191,7 @@ def diffusion_post_saved(sender, instance, created, *args, **kwargs):
page = models.DiffusionPage.from_diffusion( page = models.DiffusionPage.from_diffusion(
instance, live = False instance, live = False
) )
instance.program.page.first().add_child( instance.program.page.add_child(
instance = page instance = page
) )

View File

@ -127,8 +127,8 @@ class LogAdmin(ModelAdmin):
menu_label = _('Logs') menu_label = _('Logs')
menu_icon = 'time' menu_icon = 'time'
menu_order = 300 menu_order = 300
list_display = ['date', 'station', 'source', 'type', 'comment', 'related'] list_display = ['id', 'date', 'station', 'source', 'type', 'comment', 'diffusion', 'sound', 'track']
list_filter = ['date', 'source', 'related_type'] list_filter = ['date', 'source', 'diffusion', 'sound', 'track']
aircox.models.Log.panels = [ aircox.models.Log.panels = [
MultiFieldPanel([ MultiFieldPanel([
@ -139,11 +139,12 @@ aircox.models.Log.panels = [
]), ]),
FieldPanel('type'), FieldPanel('type'),
FieldPanel('comment'), FieldPanel('comment'),
FieldRowPanel([
FieldPanel('related_type'),
FieldPanel('related_id')
]),
], heading = _('Log')), ], heading = _('Log')),
MultiFieldPanel([
FieldPanel('diffusion'),
FieldPanel('sound'),
FieldPanel('track'),
], heading = _('Related objects')),
] ]
@ -327,7 +328,7 @@ class TodayMenu(GenericMenu):
return MenuItem(label, self.page_url(item), attrs = attrs) return MenuItem(label, self.page_url(item), attrs = attrs)
def get_parent(self, item): def get_parent(self, item):
return item.program.page.first() return item.program.page
@hooks.register('register_admin_menu_item') @hooks.register('register_admin_menu_item')