diff --git a/aircox/admin.py b/aircox/admin.py index eb807a9..dedeace 100755 --- a/aircox/admin.py +++ b/aircox/admin.py @@ -183,8 +183,8 @@ class StationAdmin(admin.ModelAdmin): @admin.register(Log) class LogAdmin(admin.ModelAdmin): - list_display = ['id', 'date', 'station', 'source', 'type', 'comment', 'related'] - list_filter = ['date', 'source', 'related_type'] + list_display = ['id', 'date', 'station', 'source', 'type', 'comment', 'diffusion', 'sound', 'track'] + list_filter = ['date', 'source', 'diffusion', 'sound', 'track'] admin.site.register(Port) diff --git a/aircox/management/commands/streamer.py b/aircox/management/commands/streamer.py index 05bf115..242df5b 100755 --- a/aircox/management/commands/streamer.py +++ b/aircox/management/commands/streamer.py @@ -15,7 +15,14 @@ from django.conf import settings as main_settings from django.core.management.base import BaseCommand, CommandError 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: @@ -65,11 +72,12 @@ class Monitor: self.sync_playlists() self.handle() - def log(self, **kwargs): + def log(self, date = None, **kwargs): """ 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.print() @@ -84,17 +92,18 @@ class Monitor: if not current_sound or not current_source: 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() - # only streamed - if log and (log.related and not log.related.diffusion): + # only streamed ns + if log and not log.sound.diffusion: self.trace_sound_tracks(log) # TODO: expiration if log and (log.source == current_source.id and \ - log.related and - log.related.path == current_sound): + log.sound and + log.sound.path == current_sound): return sound = Sound.objects.filter(path = current_sound) @@ -102,7 +111,7 @@ class Monitor: type = Log.Type.play, source = current_source.id, 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) comment = current_sound, ) @@ -112,11 +121,12 @@ class Monitor: Log tracks for the given sound (for streamed programs); Called by self.trace """ - logs = Log.objects.get_for(self.station, model = Track) \ - .filter(pk__gt = log.pk) - logs = [ log.related_id for log in logs ] + logs = Log.objects.station(self.station, + track__isnull = False, + 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) if tracks and len(tracks) == len(logs): return @@ -130,7 +140,7 @@ class Monitor: type = Log.Type.play, source = log.source, date = pos, - related = track, + track = track, comment = track, ) @@ -147,9 +157,9 @@ class Monitor: for source in self.station.sources: if source == self.station.dealer: continue - playlist = [ sound.path for sound in - source.program.sound_set.all() ] - source.playlist = playlist + playlist = source.program.sound_set.all() \ + .values_list('path', flat = True) + source.playlist = list(playlist) def trace_canceled(self): """ @@ -163,18 +173,18 @@ class Monitor: type = Diffusion.Type.normal, 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) for diff in diffs: - if logs.filter(related = diff): + if logs.filter(diffusion = diff): continue if diff.start < now: diff.type = Diffusion.Type.canceled diff.save() self.log( type = Log.Type.other, - related = diff, + diffusion = diff, comment = 'Diffusion canceled after {} seconds' \ .format(self.cancel_timeout) ) @@ -187,53 +197,81 @@ class Monitor: station = self.station now = tz.now() - diff_log = station.played(models = Diffusion) \ - .order_by('date').last() - if not diff_log or \ - not diff_log.related.is_date_in_range(now): + log = station.played(diffusion__isnull = False) \ + .select_related('diffusion') \ + .order_by('date').last() + if not log or not log.diffusion.is_date_in_range(now): + # not running anymore return None, [] - # sound has switched? assume it has been (forced to) stopped - sounds = station.played(models = Sound) \ - .filter(date__gte = diff_log.date) \ + # last sound source change: end of file reached or forced to stop + sounds = station.played(sound__isnull = False) \ + .filter(date__gte = log.date) \ .order_by('date') - if sounds.last() and sounds.last().source != diff_log.source: - return diff_log, [] + if sounds.count() and sounds.last().source != log.source: + return None, [] - # last diff is still playing: get the remaining playlist - sounds = sounds.filter( - source = diff_log.source, pk__gt = diff_log.pk - ) - sounds = [ - sound.related.path for sound in sounds - if sound.related.type != Sound.Type.removed - ] + # last diff is still playing: get remaining playlist + sounds = sounds \ + .filter(source = log.source, pk__gt = log.pk) \ + .exclude(sound__type = Sound.Type.removed) - return ( - diff_log.related, - [ path for path in diff_log.related.playlist - if path not in sounds ] - ) + remaining = log.diffusion.get_archives().exclude(pk__in = sounds) \ + .values_list('path', flat = True) + return log.diffusion, list(remaining) def __next_diff(self, diff): """ - Return the tuple with the next diff that should be played and - the playlist - - Note: diff is a log + Return the next diffusion to be played as tuple of (diff, playlist). + If diff is given, it is the one to be played right after it. """ station = self.station now = tz.now() - args = {'start__gt': diff.date } if diff else {} - diff = Diffusion.objects.at(station, now).filter( - type = Diffusion.Type.normal, - sound__type = Sound.Type.archive, - **args - ).distinct().order_by('start').first() + kwargs = {'start__gte': diff.end } if diff else {} + diff = Diffusion.objects \ + .at(station, now) \ + .filter(type = Diffusion.Type.normal, **kwargs) \ + .distinct().order_by('start') + diff = diff.first() 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): """ Handle scheduled diffusion, trigger if needed, preload playlists @@ -246,33 +284,15 @@ class Monitor: now = tz.now() # current and next diffs - diff, playlist = self.__current_diff() - dealer.active = bool(playlist) + current_diff, remaining_pl = self.__current_diff() + next_diff, next_pl = self.__next_diff(current_diff) - next_diff, next_playlist = self.__next_diff(diff) - playlist += next_playlist + # playlist + dealer.active = bool(remaining_pl) + playlist = remaining_pl + next_pl - # playlist update - if dealer.playlist != playlist: - 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, - ) + self.handle_pl_sync(dealer, playlist, next_diff, now) + self.handle_diff_start(dealer, next_diff, now) class Command (BaseCommand): diff --git a/aircox/models.py b/aircox/models.py index 3a7bdca..dde706f 100755 --- a/aircox/models.py +++ b/aircox/models.py @@ -50,7 +50,6 @@ class RelatedManager(models.Manager): qs = qs.filter(related_id = object.pk) return qs - class Related(models.Model): """ Add a field "related" of type GenericForeignKey, plus utilities. @@ -148,7 +147,6 @@ class Track(Related): verbose_name = _('Track') verbose_name_plural = _('Tracks') - # # Station related classes # @@ -180,7 +178,7 @@ class Station(Nameable): __dealer = None __streamer = None - def __prepare(self): + def __prepare_controls(self): import aircox.controllers as controllers if not self.__streamer: self.__streamer = controllers.Streamer(station = self) @@ -215,12 +213,12 @@ class Station(Nameable): """ Audio sources, dealer included """ - self.__prepare() + self.__prepare_controls() return self.__sources @property def dealer(self): - self.__prepare() + self.__prepare_controls() return self.__dealer @property @@ -228,86 +226,25 @@ class Station(Nameable): """ Audio controller for the station """ - self.__prepare() + self.__prepare_controls() return self.__streamer - def played(self, models, archives = True): + def played(self, *args, **kwargs): """ Call Log.objects.played for this station """ - return Log.objects.played(self, models, archives) - - @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 + return Log.objects.played(self, *args, **kwargs) def on_air(self, date = None, count = 0): """ - Return a list of what happened on air, based on logs and - diffusions informations. The list is sorted by -date. + Return a queryset of what happened on air, based on logs and + diffusions informations. The queryset is sorted by -date. * date: only for what happened on this date; * count: number of items to retrieve if not zero; 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. - - The list contains: - * track logs: for the streamed programs; - * diffusion: for the scheduled diffusions; """ # FIXME: as an iterator? # TODO argument to get sound instead of tracks @@ -315,21 +252,39 @@ class Station(Nameable): raise ValueError('at least one argument must be set') # 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(): return [] if date: - logs = Log.objects.at_for(self, date, model = Track) - diffs = Diffusion.objects.at(self, date) + logs = Log.objects.at(self, date) + diffs = Diffusion.objects.at(self, date, type = Diffusion.Type.normal) \ + .order_by('-start') else: - logs = Log.objects.get_for(self, model = Track) - diffs = Diffusion.objects - logs = logs.filter(station = self) + logs = Log.objects + diffs = Diffusion.objects.filter(type = Diffusion.Type.normal, + start__lte = tz.now()) \ + .order_by('-start')[:count] - diffs = diffs.filter(program__station = self) \ - .filter(type = Diffusion.Type.normal) \ - .filter(start__lte = tz.now()) - return self.__mix_logs_and_diff(diffs, logs, count) + q = models.Q(diffusion__isnull = False) | \ + models.Q(track__isnull = False) + logs = logs.filter(q).order_by('-date') + + # 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): if not self.path: @@ -348,9 +303,9 @@ class Station(Nameable): 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 - return qs.filter(station = station) + return qs.filter(station = station, **kwargs) class Program(Nameable): """ @@ -730,11 +685,11 @@ class Schedule(models.Model): 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 - 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 @@ -770,7 +725,7 @@ class DiffusionManager(models.Manager): if next: # include also diffusions of the next day filters |= models.Q(start__gte = start) - qs = qs.filter(filters) + qs = qs.filter(filters, **kwargs) return self.station(station, qs).order_by('start').distinct() def after(self, station, date = None, qs = None): @@ -867,7 +822,12 @@ class Diffusion(models.Model): """ 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): """ @@ -1229,59 +1189,51 @@ class Port (models.Model): ) -class LogManager(RelatedManager): - def station(self, station, qs = None): +class LogManager(models.Manager): + def station(self, station, qs = None, **kwargs): 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): - qs = super().get_for(*args, **kwargs) - return self.station(station, qs) if station else qs - - def _at(self, date = None, qs = None): + def _at(self, date = None, qs = None, **kwargs): start, end = utils.date_range(date) qs = self if qs is None else qs - return qs.filter(date__gte = start, - date__lte = end) + return qs.filter(date__gte = start, date__lte = end, **kwargs) - 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 in their range. """ - qs = self._at(date, qs) + qs = self._at(date, qs, **kwargs) return self.station(station, qs) if station else qs - def at_for(self, station, date, object = None, model = None, qs = None): - """ - 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): + def played(self, station, archives = True, include_live = True, + **kwargs): """ Return a queryset of the played elements' log for the given station and model. This queryset is ordered by date ascending * station: related station - * models: a model or a list of models * archives: if false, exclude log of diffusion's archives from the queryset; + * include_live: include diffusion that have no archive + * kwargs: extra filter kwargs """ - qs = self.get_for(station, model = models) \ - .filter(type = Log.Type.play) + if include_live: + 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: qs = qs.exclude( source = station.dealer.id, - related_type = ContentType.objects.get_for_model(Sound) + sound__isnull = False ) return qs.order_by('date') -class Log(Related): +class Log(models.Model): """ Log sounds and diffusions that are played on the station. @@ -1303,14 +1255,18 @@ class Log(Related): """ 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 """ type = models.SmallIntegerField( 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, ) station = models.ForeignKey( @@ -1329,6 +1285,7 @@ class Log(Related): date = models.DateTimeField( _('date'), default=tz.now, + db_index = True, ) comment = models.CharField( _('comment'), @@ -1336,6 +1293,25 @@ class Log(Related): 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() @property @@ -1343,12 +1319,16 @@ class Log(Related): """ Calculated end using self.related informations """ - if self.related_type == Diffusion: - return self.related.end - if self.related_type == Sound: - return self.date + to_timedelta(self.duration) + if self.diffusion: + return self.diffusion.end + if self.sound: + return self.date + to_timedelta(sound.duration) return self.date + @property + def related(self): + return self.diffusion or self.sound or self.track + def is_expired(self, date = None): """ Return True if the log is expired. Note that it only check @@ -1359,11 +1339,19 @@ class Log(Related): return self.end < date 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', str(self), self.comment or '', - ' -- {} #{}'.format(self.related_type, self.related_id) - if self.related else '' + ' (' + ', '.join(r) + ')' if r else '' ) def __str__(self): @@ -1371,4 +1359,3 @@ class Log(Related): self.pk, self.date.strftime('%Y/%m/%d %H:%M'), self.source ) - diff --git a/aircox/views.py b/aircox/views.py index 1e45e93..3c9c3f7 100755 --- a/aircox/views.py +++ b/aircox/views.py @@ -40,12 +40,12 @@ def on_air(request): else: station = stations.stations.first() - last = station.on_air(count = 10) - if not last: + on_air = station.on_air(count = 10).select_related('track','diffusion') + if not on_air.count(): return HttpResponse('') - last = last[0] - if type(last) == models.Log: + last = on_air.last() + if last.track: last = { 'type': 'track', 'artist': last.related.artist, @@ -54,11 +54,12 @@ def on_air(request): } else: try: + diff = last.diffusion publication = None if cms: publication = \ cms.DiffusionPage.objects.filter( - diffusion = last.initial or last).first() or \ + diffusion = diff.initial or diff).first() or \ cms.ProgramPage.objects.filter( program = last.program).first() except: @@ -66,8 +67,8 @@ def on_air(request): last = { 'type': 'diffusion', - 'title': last.program.name, - 'date': last.start, + 'title': diff.program.name, + 'date': diff.start, 'url': publication.specific.url if publication else None, } diff --git a/aircox_cms/management/commands/programs_to_cms.py b/aircox_cms/management/commands/programs_to_cms.py index 3ef1a58..171fbfe 100755 --- a/aircox_cms/management/commands/programs_to_cms.py +++ b/aircox_cms/management/commands/programs_to_cms.py @@ -64,7 +64,7 @@ class Command (BaseCommand): initial__isnull = True ).exclude(type = Diffusion.Type.unconfirmed) for diffusion in qs: - if not diffusion.program.page.count(): + if not diffusion.program.page: if not hasattr(diffusion.program, '__logged_diff_error'): logger.warning( 'the program {} has no page; skip the creation of ' @@ -80,7 +80,7 @@ class Command (BaseCommand): page = DiffusionPage.from_diffusion( diffusion, live = False ) - diffusion.program.page.first().add_child(instance = page) + diffusion.program.page.add_child(instance = page) except: import sys e = sys.exc_info()[0] diff --git a/aircox_cms/models.py b/aircox_cms/models.py index ca28cff..42ad6e6 100755 --- a/aircox_cms/models.py +++ b/aircox_cms/models.py @@ -452,10 +452,8 @@ class ProgramPage(Publication): def diffs_to_page(self, diffs): for diff in diffs: - if diff.page.count(): - diff.page_ = diff.page.first() - else: - diff.page_ = ListItem( + if not diff.page: + diff.page = ListItem( title = '{}, {}'.format( self.program.name, diff.date.strftime('%d %B %Y') ), @@ -464,7 +462,7 @@ class ProgramPage(Publication): date = diff.start, ) return [ - diff.page_ for diff in diffs if diff.page_.live + diff.page for diff in diffs if diff.page.live ] @property @@ -560,8 +558,8 @@ class DiffusionPage(Publication): 'title': '{}, {}'.format( diff.program.name, tz.localtime(diff.date).strftime('%d %B %Y') ), - 'cover': (diff.program.page.count() and \ - diff.program.page.first().cover) or None, + 'cover': (diff.program.page and \ + diff.program.page.cover) or None, 'date': diff.start, } model_kwargs.update(kwargs) @@ -637,7 +635,7 @@ class DiffusionPage(Publication): if self.diffusion: # set publish_as if not self.pk: - self.publish_as = self.diffusion.program.page.first() + self.publish_as = self.diffusion.program.page # sync date self.date = self.diffusion.start @@ -777,8 +775,9 @@ class LogsPage(DatedListPage): logs = [] for date in context['nav_dates']['dates']: - items = [ SectionLogsList.as_item(item) - for item in self.station.on_air(date = date) ] + items = self.station.on_air(date = date) \ + .select_related('track','diffusion') + items = [ SectionLogsList.as_item(item) for item in items ] logs.append( (date, reversed(items) if self.reverse else items) ) diff --git a/aircox_cms/sections.py b/aircox_cms/sections.py index d6e8f65..98b9227 100755 --- a/aircox_cms/sections.py +++ b/aircox_cms/sections.py @@ -957,16 +957,16 @@ class SectionLogsList(SectionItem): Supports: Log/Track, Diffusion """ from aircox_cms.models import DiffusionPage - if type(log) == aircox.models.Diffusion: - return DiffusionPage.as_item(log) + if log.diffusion: + return DiffusionPage.as_item(log.diffusion) - related = log.related + track = log.track return ListItem( title = '{artist} -- {title}'.format( - artist = related.artist, - title = related.title, + artist = track.artist, + title = track.title, ), - headline = related.info, + headline = track.info, date = log.date, info = '♫', css_class = 'track' diff --git a/aircox_cms/signals.py b/aircox_cms/signals.py index b43e4d6..2588098 100755 --- a/aircox_cms/signals.py +++ b/aircox_cms/signals.py @@ -118,7 +118,7 @@ def station_post_saved(sender, instance, created, *args, **kwargs): @receiver(post_save, sender=aircox.Program) def program_post_saved(sender, instance, created, *args, **kwargs): - if not created or instance.page.count(): + if not created or instance.page: return 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( instance, live = False ) - instance.program.page.first().add_child( + instance.program.page.add_child( instance = page ) diff --git a/aircox_cms/wagtail_hooks.py b/aircox_cms/wagtail_hooks.py index 48f800e..751e938 100755 --- a/aircox_cms/wagtail_hooks.py +++ b/aircox_cms/wagtail_hooks.py @@ -127,8 +127,8 @@ class LogAdmin(ModelAdmin): menu_label = _('Logs') menu_icon = 'time' menu_order = 300 - list_display = ['date', 'station', 'source', 'type', 'comment', 'related'] - list_filter = ['date', 'source', 'related_type'] + list_display = ['id', 'date', 'station', 'source', 'type', 'comment', 'diffusion', 'sound', 'track'] + list_filter = ['date', 'source', 'diffusion', 'sound', 'track'] aircox.models.Log.panels = [ MultiFieldPanel([ @@ -139,11 +139,12 @@ aircox.models.Log.panels = [ ]), FieldPanel('type'), FieldPanel('comment'), - FieldRowPanel([ - FieldPanel('related_type'), - FieldPanel('related_id') - ]), ], 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) def get_parent(self, item): - return item.program.page.first() + return item.program.page @hooks.register('register_admin_menu_item')