fix models manager and qs; schedule_post_saved
This commit is contained in:
		@ -164,9 +164,18 @@ class ScheduleAdmin(admin.ModelAdmin):
 | 
			
		||||
    rerun.short_description = _('Rerun')
 | 
			
		||||
    rerun.boolean = True
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    list_filter = ['frequency', 'program']
 | 
			
		||||
    list_display = ['id', 'program_name', 'frequency', 'day', 'date', 'time', 'timezone', 'duration', 'rerun']
 | 
			
		||||
    list_editable = ['frequency', 'date', 'time', 'timezone', 'duration']
 | 
			
		||||
    list_display = ['id', 'program_name', 'frequency', 'day', 'date',
 | 
			
		||||
                    '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)
 | 
			
		||||
 | 
			
		||||
@ -172,12 +172,9 @@ class SoundInfo:
 | 
			
		||||
                                     self.hour or 0, self.minute or 0)
 | 
			
		||||
            date = tz.get_current_timezone().localize(date)
 | 
			
		||||
 | 
			
		||||
        diffusion = Diffusion.objects.after(
 | 
			
		||||
            program.station,
 | 
			
		||||
            date,
 | 
			
		||||
            program = program,
 | 
			
		||||
            initial__isnull = True,
 | 
			
		||||
        ).first()
 | 
			
		||||
        qs = Diffusion.objects.station(program.station).after(date) \
 | 
			
		||||
                      .filter(program = program, initial__isnull = True)
 | 
			
		||||
        diffusion = qs.first()
 | 
			
		||||
        if not diffusion:
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -245,14 +245,14 @@ class Monitor:
 | 
			
		||||
        if not self.cancel_timeout:
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        diffs = Diffusions.objects.at(self.station).filter(
 | 
			
		||||
        qs = Diffusions.objects.station(self.station).at().filter(
 | 
			
		||||
            type = Diffusion.Type.normal,
 | 
			
		||||
            sound__type = Sound.Type.archive,
 | 
			
		||||
        )
 | 
			
		||||
        logs = station.raw_on_air(diffusion__isnull = False)
 | 
			
		||||
 | 
			
		||||
        date = tz.now() - datetime.timedelta(seconds = self.cancel_timeout)
 | 
			
		||||
        for diff in diffs:
 | 
			
		||||
        for diff in qs:
 | 
			
		||||
            if logs.filter(diffusion = diff):
 | 
			
		||||
                continue
 | 
			
		||||
            if diff.start < now:
 | 
			
		||||
@ -305,14 +305,13 @@ class Monitor:
 | 
			
		||||
        If diff is given, it is the one to be played right after it.
 | 
			
		||||
        """
 | 
			
		||||
        station = self.station
 | 
			
		||||
        now = tz.now()
 | 
			
		||||
 | 
			
		||||
        kwargs = {'start__gte': diff.end } if diff else {}
 | 
			
		||||
        diff = Diffusion.objects \
 | 
			
		||||
            .at(station, now) \
 | 
			
		||||
            .filter(type = Diffusion.Type.normal, **kwargs) \
 | 
			
		||||
        kwargs['type'] = Diffusion.Type.normal
 | 
			
		||||
 | 
			
		||||
        qs = Diffusion.objects.station(station).at().filter(**kwargs) \
 | 
			
		||||
                      .distinct().order_by('start')
 | 
			
		||||
        diff = diff.first()
 | 
			
		||||
        diff = qs.first()
 | 
			
		||||
        return (diff, diff and diff.get_playlist(archive = True) or [])
 | 
			
		||||
 | 
			
		||||
    def handle_pl_sync(self, source, playlist, diff = None, date = None):
 | 
			
		||||
 | 
			
		||||
@ -232,11 +232,11 @@ class Station(Nameable):
 | 
			
		||||
        self.__prepare_controls()
 | 
			
		||||
        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
 | 
			
		||||
        """
 | 
			
		||||
        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):
 | 
			
		||||
        """
 | 
			
		||||
@ -266,10 +266,9 @@ class Station(Nameable):
 | 
			
		||||
 | 
			
		||||
        now = tz.now()
 | 
			
		||||
        if date:
 | 
			
		||||
            logs = Log.objects.at(self, date)
 | 
			
		||||
            diffs = Diffusion.objects \
 | 
			
		||||
                             .at(self, date, type = Diffusion.Type.normal) \
 | 
			
		||||
                             .filter(start__lte = now) \
 | 
			
		||||
            logs = Log.objects.station(self).at(date)
 | 
			
		||||
            diffs = Diffusion.objects.station(self).at(date) \
 | 
			
		||||
                        .filter(start__lte = now, type = Diffusion.Type.normal) \
 | 
			
		||||
                        .order_by('-start')
 | 
			
		||||
        else:
 | 
			
		||||
            logs = Log.objects
 | 
			
		||||
@ -568,15 +567,11 @@ class Schedule(models.Model):
 | 
			
		||||
        if not initial:
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        before, now = self.__initial, self.__dict__
 | 
			
		||||
        before, now = {
 | 
			
		||||
            f: getattr(before, f) for f in fields
 | 
			
		||||
            if hasattr(before, f)
 | 
			
		||||
        }, {
 | 
			
		||||
            f: getattr(now, f) for f in fields
 | 
			
		||||
            if hasattr(now, f)
 | 
			
		||||
        }
 | 
			
		||||
        return before == now
 | 
			
		||||
        this = self.__dict__
 | 
			
		||||
        for field in fields:
 | 
			
		||||
            if initial.get(field) != this.get(field):
 | 
			
		||||
                return True
 | 
			
		||||
        return False
 | 
			
		||||
 | 
			
		||||
    def match(self, date = None, check_time = True):
 | 
			
		||||
        """
 | 
			
		||||
@ -727,6 +722,9 @@ class Schedule(models.Model):
 | 
			
		||||
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        super().__init__(*args, **kwargs)
 | 
			
		||||
 | 
			
		||||
        # initial only if it has been yet saved
 | 
			
		||||
        if self.pk:
 | 
			
		||||
            self.__initial = self.__dict__.copy()
 | 
			
		||||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
@ -747,12 +745,14 @@ class Schedule(models.Model):
 | 
			
		||||
        verbose_name_plural = _('Schedules')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DiffusionManager(models.Manager):
 | 
			
		||||
    def station(self, station, qs = None, **kwargs):
 | 
			
		||||
        qs = self if qs is None else qs
 | 
			
		||||
        return qs.filter(program__station = station, **kwargs)
 | 
			
		||||
class DiffusionQuerySet(models.QuerySet):
 | 
			
		||||
    def station(self, station, **kwargs):
 | 
			
		||||
        return self.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
 | 
			
		||||
 | 
			
		||||
@ -768,7 +768,7 @@ class DiffusionManager(models.Manager):
 | 
			
		||||
        # note: we work with localtime
 | 
			
		||||
        date = utils.date_or_default(date, keep_type = True)
 | 
			
		||||
 | 
			
		||||
        qs = self if qs is None else qs
 | 
			
		||||
        qs = self
 | 
			
		||||
        filters = None
 | 
			
		||||
        if isinstance(date, datetime.datetime):
 | 
			
		||||
            # use datetime: we want diffusion that occurs around this
 | 
			
		||||
@ -789,25 +789,23 @@ class DiffusionManager(models.Manager):
 | 
			
		||||
                # include also diffusions of the next day
 | 
			
		||||
                filters |= models.Q(start__gte = start)
 | 
			
		||||
            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
 | 
			
		||||
        date.
 | 
			
		||||
        """
 | 
			
		||||
        date = utils.date_or_default(date, keep_type = True)
 | 
			
		||||
        return self.station(station, start__gte = date, **kwargs)\
 | 
			
		||||
                   .order_by('start')
 | 
			
		||||
        return self.filter(start__gte = date, **kwargs).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
 | 
			
		||||
        date.
 | 
			
		||||
        """
 | 
			
		||||
        date = utils.date_or_default(date)
 | 
			
		||||
        return self.station(station, end__lte = date, **kwargs) \
 | 
			
		||||
                   .order_by('start')
 | 
			
		||||
        return self.filter(end__lte = date, **kwargs).order_by('start')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Diffusion(models.Model):
 | 
			
		||||
@ -828,7 +826,7 @@ class Diffusion(models.Model):
 | 
			
		||||
    - cancel: the diffusion has been canceled
 | 
			
		||||
    - stop: the diffusion has been manually stopped
 | 
			
		||||
    """
 | 
			
		||||
    objects = DiffusionManager()
 | 
			
		||||
    objects = DiffusionQuerySet.as_manager()
 | 
			
		||||
 | 
			
		||||
    class Type(IntEnum):
 | 
			
		||||
        normal = 0x00
 | 
			
		||||
@ -1281,26 +1279,18 @@ class Port (models.Model):
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class LogManager(models.Manager):
 | 
			
		||||
    def station(self, station, *args, **kwargs):
 | 
			
		||||
        return self.filter(*args, station = station, **kwargs)
 | 
			
		||||
class LogQuerySet(models.QuerySet):
 | 
			
		||||
    def station(self, station):
 | 
			
		||||
        return self.filter(station = station)
 | 
			
		||||
 | 
			
		||||
    def _at(self, date = None, *args, **kwargs):
 | 
			
		||||
    def at(self, date = None):
 | 
			
		||||
        start, end = utils.date_range(date)
 | 
			
		||||
        # return qs.filter(models.Q(end__gte = start) |
 | 
			
		||||
        #                 models.Q(date__lte = end))
 | 
			
		||||
        return self.filter(*args, date__gte = start, date__lte = end, **kwargs)
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
        return self.filter(date__gte = start, date__lte = end)
 | 
			
		||||
 | 
			
		||||
    # 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
 | 
			
		||||
        station and model. This queryset is ordered by date ascending
 | 
			
		||||
@ -1310,11 +1300,11 @@ class LogManager(models.Manager):
 | 
			
		||||
        * kwargs: extra filter kwargs
 | 
			
		||||
        """
 | 
			
		||||
        if date:
 | 
			
		||||
            qs = self.at(station, date)
 | 
			
		||||
            qs = self.at(date)
 | 
			
		||||
        else:
 | 
			
		||||
            qs = self
 | 
			
		||||
 | 
			
		||||
        qs = qs.filter(type = Log.Type.on_air, **kwargs)
 | 
			
		||||
        qs = qs.filter(type = Log.Type.on_air)
 | 
			
		||||
        return qs.order_by('date')
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
@ -1399,7 +1389,7 @@ class LogManager(models.Manager):
 | 
			
		||||
        if os.path.exists(path) and not force:
 | 
			
		||||
            return -1
 | 
			
		||||
 | 
			
		||||
        qs = self.at(station, date)
 | 
			
		||||
        qs = self.station(station).at(date)
 | 
			
		||||
        if not qs.exists():
 | 
			
		||||
            return 0
 | 
			
		||||
 | 
			
		||||
@ -1516,7 +1506,7 @@ class Log(models.Model):
 | 
			
		||||
        on_delete=models.SET_NULL,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    objects = LogManager()
 | 
			
		||||
    objects = LogQuerySet.as_manager()
 | 
			
		||||
 | 
			
		||||
    def estimate_end(self):
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
@ -39,62 +39,44 @@ def user_default_groups(sender, instance, created, *args, **kwargs):
 | 
			
		||||
        instance.groups.add(group)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# FIXME: avoid copy of the code in schedule_post_saved and
 | 
			
		||||
#        schedule_pre_delete
 | 
			
		||||
 | 
			
		||||
@receiver(post_save, sender=models.Schedule)
 | 
			
		||||
def schedule_post_saved(sender, instance, created, *args, **kwargs):
 | 
			
		||||
    return
 | 
			
		||||
    # TODO: case instance.program | instance.frequency has changed
 | 
			
		||||
    if not instance.program.sync:
 | 
			
		||||
def schedule_post_save(sender, instance, created, *args, **kwargs):
 | 
			
		||||
    """
 | 
			
		||||
    Handles Schedule's time, duration and timezone changes and update
 | 
			
		||||
    corresponding diffusions accordingly.
 | 
			
		||||
    """
 | 
			
		||||
    if created or not instance.program.sync or \
 | 
			
		||||
            not instance.changed(['time','duration','timezone']):
 | 
			
		||||
        return
 | 
			
		||||
 | 
			
		||||
    initial = instance._Schedule__initial
 | 
			
		||||
    if not initial or not instance.changed(['date','duration', 'frequency']):
 | 
			
		||||
        return
 | 
			
		||||
 | 
			
		||||
    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')
 | 
			
		||||
    initial = models.Schedule(**{ k: v
 | 
			
		||||
        for k, v in instance._Schedule__initial.items()
 | 
			
		||||
            if not k.startswith('_')
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    # change: day, time, duration, frequence => TODO
 | 
			
		||||
    delta = (instance.date - old.date) + \
 | 
			
		||||
            (instance.time - old.time)
 | 
			
		||||
    today = tz.datetime.today()
 | 
			
		||||
    delta = instance.normalize(today) - \
 | 
			
		||||
            initial.normalize(today)
 | 
			
		||||
 | 
			
		||||
    qs = models.Diffusion.objects.station(
 | 
			
		||||
        instance.program.station,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    pks = [ item.pk for item in qs if old.match(item.date) ]
 | 
			
		||||
    qs = models.Diffusion.objects.program(instance.program).after()
 | 
			
		||||
    pks = [ d.pk for d in qs if initial.match(d.date) ]
 | 
			
		||||
    qs.filter(pk__in = pks).update(
 | 
			
		||||
        start = F('start') + delta,
 | 
			
		||||
        end = F('start') + delta + utils.to_timedelta(instance.duration)
 | 
			
		||||
    )
 | 
			
		||||
    return
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@receiver(pre_delete, sender=models.Schedule)
 | 
			
		||||
def schedule_pre_delete(sender, instance, *args, **kwargs):
 | 
			
		||||
    """
 | 
			
		||||
    Delete later corresponding diffusion to a changed schedule.
 | 
			
		||||
    """
 | 
			
		||||
    if not instance.program.sync:
 | 
			
		||||
        return
 | 
			
		||||
 | 
			
		||||
    initial = instance._Schedule__initial
 | 
			
		||||
    if not initial or not instance.changed(['date','duration', 'frequency']):
 | 
			
		||||
        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 = models.Diffusion.objects.program(instance.program).after()
 | 
			
		||||
    pks = [ d.pk for d in qs if instance.match(d.date) ]
 | 
			
		||||
    qs.filter(pk__in = pks).delete()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user