fix models manager and qs; schedule_post_saved

This commit is contained in:
bkfox 2018-06-27 18:08:05 +02:00
parent 04c3330c21
commit f70b692aff
5 changed files with 82 additions and 105 deletions

View File

@ -164,9 +164,18 @@ class ScheduleAdmin(admin.ModelAdmin):
rerun.short_description = _('Rerun') rerun.short_description = _('Rerun')
rerun.boolean = True rerun.boolean = True
list_filter = ['frequency', 'program'] list_filter = ['frequency', 'program']
list_display = ['id', 'program_name', 'frequency', 'day', 'date', 'time', 'timezone', 'duration', 'rerun'] list_display = ['id', 'program_name', 'frequency', 'day', 'date',
list_editable = ['frequency', 'date', 'time', 'timezone', 'duration'] 'time', 'duration', 'timezone', 'rerun']
list_editable = ['time', 'timezone', 'duration']
def get_readonly_fields(self, request, obj=None):
if obj:
return ['program', 'date', 'frequency']
else:
return []
@admin.register(Track) @admin.register(Track)

View File

@ -172,12 +172,9 @@ class SoundInfo:
self.hour or 0, self.minute or 0) self.hour or 0, self.minute or 0)
date = tz.get_current_timezone().localize(date) date = tz.get_current_timezone().localize(date)
diffusion = Diffusion.objects.after( qs = Diffusion.objects.station(program.station).after(date) \
program.station, .filter(program = program, initial__isnull = True)
date, diffusion = qs.first()
program = program,
initial__isnull = True,
).first()
if not diffusion: if not diffusion:
return return

View File

@ -245,14 +245,14 @@ class Monitor:
if not self.cancel_timeout: if not self.cancel_timeout:
return return
diffs = Diffusions.objects.at(self.station).filter( qs = Diffusions.objects.station(self.station).at().filter(
type = Diffusion.Type.normal, type = Diffusion.Type.normal,
sound__type = Sound.Type.archive, sound__type = Sound.Type.archive,
) )
logs = station.raw_on_air(diffusion__isnull = False) logs = station.raw_on_air(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 qs:
if logs.filter(diffusion = diff): if logs.filter(diffusion = diff):
continue continue
if diff.start < now: if diff.start < now:
@ -305,14 +305,13 @@ class Monitor:
If diff is given, it is the one to be played right after it. If diff is given, it is the one to be played right after it.
""" """
station = self.station station = self.station
now = tz.now()
kwargs = {'start__gte': diff.end } if diff else {} kwargs = {'start__gte': diff.end } if diff else {}
diff = Diffusion.objects \ kwargs['type'] = Diffusion.Type.normal
.at(station, now) \
.filter(type = Diffusion.Type.normal, **kwargs) \ qs = Diffusion.objects.station(station).at().filter(**kwargs) \
.distinct().order_by('start') .distinct().order_by('start')
diff = diff.first() diff = qs.first()
return (diff, diff and diff.get_playlist(archive = True) or []) return (diff, diff and diff.get_playlist(archive = True) or [])
def handle_pl_sync(self, source, playlist, diff = None, date = None): def handle_pl_sync(self, source, playlist, diff = None, date = None):

View File

@ -232,11 +232,11 @@ class Station(Nameable):
self.__prepare_controls() self.__prepare_controls()
return self.__streamer return self.__streamer
def raw_on_air(self, *args, **kwargs): def raw_on_air(self, **kwargs):
""" """
Forward call to Log.objects.on_air for this station Forward call to Log.objects.on_air for this station
""" """
return Log.objects.on_air(self, *args, **kwargs) return Log.objects.station(self).on_air().filter(**kwargs)
def on_air(self, date = None, count = 0, no_cache = False): def on_air(self, date = None, count = 0, no_cache = False):
""" """
@ -266,11 +266,10 @@ class Station(Nameable):
now = tz.now() now = tz.now()
if date: if date:
logs = Log.objects.at(self, date) logs = Log.objects.station(self).at(date)
diffs = Diffusion.objects \ diffs = Diffusion.objects.station(self).at(date) \
.at(self, date, type = Diffusion.Type.normal) \ .filter(start__lte = now, type = Diffusion.Type.normal) \
.filter(start__lte = now) \ .order_by('-start')
.order_by('-start')
else: else:
logs = Log.objects logs = Log.objects
diffs = Diffusion.objects \ diffs = Diffusion.objects \
@ -568,15 +567,11 @@ class Schedule(models.Model):
if not initial: if not initial:
return return
before, now = self.__initial, self.__dict__ this = self.__dict__
before, now = { for field in fields:
f: getattr(before, f) for f in fields if initial.get(field) != this.get(field):
if hasattr(before, f) return True
}, { return False
f: getattr(now, f) for f in fields
if hasattr(now, f)
}
return before == now
def match(self, date = None, check_time = True): def match(self, date = None, check_time = True):
""" """
@ -727,7 +722,10 @@ class Schedule(models.Model):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.__initial = self.__dict__.copy()
# initial only if it has been yet saved
if self.pk:
self.__initial = self.__dict__.copy()
def __str__(self): def __str__(self):
return ' | '.join([ '#' + str(self.id), self.program.name, return ' | '.join([ '#' + str(self.id), self.program.name,
@ -747,12 +745,14 @@ class Schedule(models.Model):
verbose_name_plural = _('Schedules') verbose_name_plural = _('Schedules')
class DiffusionManager(models.Manager): class DiffusionQuerySet(models.QuerySet):
def station(self, station, qs = None, **kwargs): def station(self, station, **kwargs):
qs = self if qs is None else qs return self.filter(program__station = station, **kwargs)
return qs.filter(program__station = station, **kwargs)
def at(self, station, date = None, next = False, qs = None, **kwargs): def program(self, program):
return self.filter(program = program)
def at(self, date = None, next = False, **kwargs):
""" """
Return diffusions occuring at the given date, ordered by +start Return diffusions occuring at the given date, ordered by +start
@ -768,7 +768,7 @@ class DiffusionManager(models.Manager):
# note: we work with localtime # note: we work with localtime
date = utils.date_or_default(date, keep_type = True) date = utils.date_or_default(date, keep_type = True)
qs = self if qs is None else qs qs = self
filters = None filters = None
if isinstance(date, datetime.datetime): if isinstance(date, datetime.datetime):
# use datetime: we want diffusion that occurs around this # use datetime: we want diffusion that occurs around this
@ -789,25 +789,23 @@ class DiffusionManager(models.Manager):
# 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, **kwargs) qs = qs.filter(filters, **kwargs)
return self.station(station, qs).order_by('start').distinct() return qs.order_by('start').distinct()
def after(self, station, date = None, **kwargs): def after(self, date = None, **kwargs):
""" """
Return a queryset of diffusions that happen after the given Return a queryset of diffusions that happen after the given
date. date.
""" """
date = utils.date_or_default(date, keep_type = True) date = utils.date_or_default(date, keep_type = True)
return self.station(station, start__gte = date, **kwargs)\ return self.filter(start__gte = date, **kwargs).order_by('start')
.order_by('start')
def before(self, station, date = None, **kwargs): def before(self, date = None, **kwargs):
""" """
Return a queryset of diffusions that finish before the given Return a queryset of diffusions that finish before the given
date. date.
""" """
date = utils.date_or_default(date) date = utils.date_or_default(date)
return self.station(station, end__lte = date, **kwargs) \ return self.filter(end__lte = date, **kwargs).order_by('start')
.order_by('start')
class Diffusion(models.Model): class Diffusion(models.Model):
@ -828,7 +826,7 @@ class Diffusion(models.Model):
- cancel: the diffusion has been canceled - cancel: the diffusion has been canceled
- stop: the diffusion has been manually stopped - stop: the diffusion has been manually stopped
""" """
objects = DiffusionManager() objects = DiffusionQuerySet.as_manager()
class Type(IntEnum): class Type(IntEnum):
normal = 0x00 normal = 0x00
@ -1281,26 +1279,18 @@ class Port (models.Model):
) )
class LogManager(models.Manager): class LogQuerySet(models.QuerySet):
def station(self, station, *args, **kwargs): def station(self, station):
return self.filter(*args, station = station, **kwargs) return self.filter(station = station)
def _at(self, date = None, *args, **kwargs): def at(self, date = None):
start, end = utils.date_range(date) start, end = utils.date_range(date)
# return qs.filter(models.Q(end__gte = start) | # return qs.filter(models.Q(end__gte = start) |
# models.Q(date__lte = end)) # models.Q(date__lte = end))
return self.filter(*args, date__gte = start, date__lte = end, **kwargs) return self.filter(date__gte = start, date__lte = end)
def at(self, station = None, date = None, *args, **kwargs):
"""
Return a queryset of logs that have the given date
in their range.
"""
qs = self._at(date, *args, **kwargs)
return qs.filter(station = station) if station else qs
# TODO: rename on_air + rename Station.on_air into sth like regular_on_air # TODO: rename on_air + rename Station.on_air into sth like regular_on_air
def on_air(self, station, date = None, **kwargs): def on_air(self, date = None):
""" """
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
@ -1310,11 +1300,11 @@ class LogManager(models.Manager):
* kwargs: extra filter kwargs * kwargs: extra filter kwargs
""" """
if date: if date:
qs = self.at(station, date) qs = self.at(date)
else: else:
qs = self qs = self
qs = qs.filter(type = Log.Type.on_air, **kwargs) qs = qs.filter(type = Log.Type.on_air)
return qs.order_by('date') return qs.order_by('date')
@staticmethod @staticmethod
@ -1399,7 +1389,7 @@ class LogManager(models.Manager):
if os.path.exists(path) and not force: if os.path.exists(path) and not force:
return -1 return -1
qs = self.at(station, date) qs = self.station(station).at(date)
if not qs.exists(): if not qs.exists():
return 0 return 0
@ -1516,7 +1506,7 @@ class Log(models.Model):
on_delete=models.SET_NULL, on_delete=models.SET_NULL,
) )
objects = LogManager() objects = LogQuerySet.as_manager()
def estimate_end(self): def estimate_end(self):
""" """

View File

@ -39,62 +39,44 @@ def user_default_groups(sender, instance, created, *args, **kwargs):
instance.groups.add(group) instance.groups.add(group)
# FIXME: avoid copy of the code in schedule_post_saved and
# schedule_pre_delete
@receiver(post_save, sender=models.Schedule) @receiver(post_save, sender=models.Schedule)
def schedule_post_saved(sender, instance, created, *args, **kwargs): def schedule_post_save(sender, instance, created, *args, **kwargs):
return """
# TODO: case instance.program | instance.frequency has changed Handles Schedule's time, duration and timezone changes and update
if not instance.program.sync: corresponding diffusions accordingly.
"""
if created or not instance.program.sync or \
not instance.changed(['time','duration','timezone']):
return return
initial = instance._Schedule__initial initial = instance._Schedule__initial
if not initial or not instance.changed(['date','duration', 'frequency']): initial = models.Schedule(**{ k: v
return for k, v in instance._Schedule__initial.items()
if not k.startswith('_')
if not initial.get('date') or not initial.get('duration') \
or not initial.get('frequency') or \
initial.frequency != instance.frequency:
return
# old schedule and timedelta
old = models.Schedule(**{ key: initial.get(key)
for key in ('date','timezone','duration','frequency')
}) })
# change: day, time, duration, frequence => TODO today = tz.datetime.today()
delta = (instance.date - old.date) + \ delta = instance.normalize(today) - \
(instance.time - old.time) initial.normalize(today)
qs = models.Diffusion.objects.station( qs = models.Diffusion.objects.program(instance.program).after()
instance.program.station, pks = [ d.pk for d in qs if initial.match(d.date) ]
)
pks = [ item.pk for item in qs if old.match(item.date) ]
qs.filter(pk__in = pks).update( qs.filter(pk__in = pks).update(
start = F('start') + delta, start = F('start') + delta,
end = F('start') + delta + utils.to_timedelta(instance.duration) end = F('start') + delta + utils.to_timedelta(instance.duration)
) )
return
@receiver(pre_delete, sender=models.Schedule) @receiver(pre_delete, sender=models.Schedule)
def schedule_pre_delete(sender, instance, *args, **kwargs): def schedule_pre_delete(sender, instance, *args, **kwargs):
"""
Delete later corresponding diffusion to a changed schedule.
"""
if not instance.program.sync: if not instance.program.sync:
return return
initial = instance._Schedule__initial qs = models.Diffusion.objects.program(instance.program).after()
if not initial or not instance.changed(['date','duration', 'frequency']): pks = [ d.pk for d in qs if instance.match(d.date) ]
return
old = models.Schedule(**{ key: initial.get(key)
for key in ('date','timezone','duration','frequency')
})
qs = models.Diffusion.objects.station(
instance.program.station,
)
pks = [ item.pk for item in qs if old.match(item.date) ]
qs.filter(pk__in = pks).delete() qs.filter(pk__in = pks).delete()