schedules algorithms
This commit is contained in:
		@ -81,7 +81,7 @@ class SoundFileAdmin (MetadataAdmin):
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    #inlines = [ EpisodeInline ]
 | 
			
		||||
    inlines = [ EventInline ]
 | 
			
		||||
    #inlines = [ EventInline ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,5 @@
 | 
			
		||||
import argparse
 | 
			
		||||
import json
 | 
			
		||||
 | 
			
		||||
from django.core.management.base    import BaseCommand, CommandError
 | 
			
		||||
from django.utils                   import timezone
 | 
			
		||||
@ -110,7 +111,7 @@ class Model:
 | 
			
		||||
                v = getattr(item, f)
 | 
			
		||||
                if hasattr(v, 'id'):
 | 
			
		||||
                    v = v.id
 | 
			
		||||
                r.append(str(v).ljust(len(f)))
 | 
			
		||||
                r.append(v)
 | 
			
		||||
            items.append(r)
 | 
			
		||||
 | 
			
		||||
        if options.get('head'):
 | 
			
		||||
@ -118,7 +119,14 @@ class Model:
 | 
			
		||||
        elif options.get('tail'):
 | 
			
		||||
            items = items[-options.get('tail'):]
 | 
			
		||||
 | 
			
		||||
        print(' || '.join(fields))
 | 
			
		||||
        if options.get('json'):
 | 
			
		||||
            if options.get('fields'):
 | 
			
		||||
                print(json.dumps(fields))
 | 
			
		||||
            print(json.dumps(items, default = lambda x: str(x)))
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        if options.get('fields'):
 | 
			
		||||
            print(' || '.join(fields))
 | 
			
		||||
        for item in items:
 | 
			
		||||
            print(' || '.join(item))
 | 
			
		||||
 | 
			
		||||
@ -165,7 +173,7 @@ models = {
 | 
			
		||||
  , 'schedule': Model( models.Schedule
 | 
			
		||||
                    , { 'parent_id': int, 'date': DateTime, 'duration': Time
 | 
			
		||||
                      , 'frequency': int }
 | 
			
		||||
                    , { 'rerun': bool }
 | 
			
		||||
                    , { 'rerun': int } # FIXME: redo
 | 
			
		||||
                    )
 | 
			
		||||
  , 'soundfile': Model( models.SoundFile
 | 
			
		||||
                    , { 'parent_id': int, 'date': DateTime, 'file': str
 | 
			
		||||
@ -194,6 +202,8 @@ class Command (BaseCommand):
 | 
			
		||||
        group.add_argument('--add', action='store_true'
 | 
			
		||||
                          , help='create or update (if id is given) object')
 | 
			
		||||
        group.add_argument('--delete', action='store_true')
 | 
			
		||||
        group.add_argument('--json', action='store_true'
 | 
			
		||||
                          , help='dump using json')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        group = parser.add_argument_group('selector')
 | 
			
		||||
@ -207,6 +217,10 @@ class Command (BaseCommand):
 | 
			
		||||
        group.add_argument('--tail', type=int
 | 
			
		||||
                          , help='dump the TAIL last objects only'
 | 
			
		||||
                          )
 | 
			
		||||
        group.add_argument('--fields', action='store_true'
 | 
			
		||||
                          , help='print fields before dumping'
 | 
			
		||||
                          )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        # publication/generic
 | 
			
		||||
        group = parser.add_argument_group('fields'
 | 
			
		||||
@ -232,7 +246,7 @@ class Command (BaseCommand):
 | 
			
		||||
        # schedule
 | 
			
		||||
        group.add_argument('--duration',   type=str)
 | 
			
		||||
        group.add_argument('--frequency',  type=int)
 | 
			
		||||
        group.add_argument('--rerun',      action='store_true')
 | 
			
		||||
        group.add_argument('--rerun',      type=int)
 | 
			
		||||
 | 
			
		||||
        # fields
 | 
			
		||||
        parser.formatter_class=argparse.RawDescriptionHelpFormatter
 | 
			
		||||
@ -258,7 +272,7 @@ class Command (BaseCommand):
 | 
			
		||||
                models[model].make(options)
 | 
			
		||||
        elif options.get('delete'):
 | 
			
		||||
            models[model].delete(options)
 | 
			
		||||
        else:
 | 
			
		||||
        else: # --dump --json
 | 
			
		||||
            models[model].dump(options)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -17,41 +17,42 @@ import programs.settings                    as settings
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Frequency for schedules. Basically, it is a mask of bits where each bit is a
 | 
			
		||||
# week. Bits > rank 5 are used for special schedules.
 | 
			
		||||
# Important: the first week is always the first week where the weekday of the
 | 
			
		||||
# schedule is present.
 | 
			
		||||
Frequency = {
 | 
			
		||||
    'ponctual':          0b000000
 | 
			
		||||
  , 'every week':        0b001111
 | 
			
		||||
  , 'first week':        0b000001
 | 
			
		||||
  , 'second week':       0b000010
 | 
			
		||||
  , 'third week':        0b000100
 | 
			
		||||
  , 'fourth week':       0b001000
 | 
			
		||||
  , 'first and third':   0b000101
 | 
			
		||||
    'ponctual':         0b000000
 | 
			
		||||
  , 'first':            0b000001
 | 
			
		||||
  , 'second':           0b000010
 | 
			
		||||
  , 'third':            0b000100
 | 
			
		||||
  , 'fourth':           0b001000
 | 
			
		||||
  , 'last':             0b010000
 | 
			
		||||
  , 'first and third':  0b000101
 | 
			
		||||
  , 'second and fourth': 0b001010
 | 
			
		||||
  , 'one week on two':   0b010010
 | 
			
		||||
    #'uneven week':         0b100000
 | 
			
		||||
    # TODO 'every day':     0b110000
 | 
			
		||||
  , 'every':            0b011111
 | 
			
		||||
  , 'one on two':       0b100000
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Translators: html safe values
 | 
			
		||||
ugettext_lazy('ponctual')
 | 
			
		||||
ugettext_lazy('every week')
 | 
			
		||||
ugettext_lazy('first week')
 | 
			
		||||
ugettext_lazy('second week')
 | 
			
		||||
ugettext_lazy('third week')
 | 
			
		||||
ugettext_lazy('fourth week')
 | 
			
		||||
ugettext_lazy('every')
 | 
			
		||||
ugettext_lazy('first')
 | 
			
		||||
ugettext_lazy('second')
 | 
			
		||||
ugettext_lazy('third')
 | 
			
		||||
ugettext_lazy('fourth')
 | 
			
		||||
ugettext_lazy('first and third')
 | 
			
		||||
ugettext_lazy('second and fourth')
 | 
			
		||||
ugettext_lazy('one week on two')
 | 
			
		||||
ugettext_lazy('one on two')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
EventType = {
 | 
			
		||||
    'play':     0x02    # the sound is playing / planified to play
 | 
			
		||||
  , 'cancel':   0x03    # the sound has been canceled from grid; useful to give
 | 
			
		||||
    'diffuse':  0x01   # the diffusion is planified or done
 | 
			
		||||
  , 'cancel':   0x03   # the diffusion has been canceled from grid; useful to give
 | 
			
		||||
                        # the info to the users
 | 
			
		||||
  , 'stop':     0x04    # the sound has been arbitrary stopped (non-stop or not)
 | 
			
		||||
  , 'non-stop': 0x05    # the sound has been played as non-stop
 | 
			
		||||
#, 'streaming'
 | 
			
		||||
  , 'stop':     0x04   # the diffusion been arbitrary stopped (non-stop or not)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -109,8 +110,9 @@ class Metadata (Model):
 | 
			
		||||
    public      = models.BooleanField(
 | 
			
		||||
                      _('public')
 | 
			
		||||
                    , default = False
 | 
			
		||||
                    , help_text = _('publication is accessible to the public')
 | 
			
		||||
                    , help_text = _('publication is public')
 | 
			
		||||
                  )
 | 
			
		||||
    # FIXME: add a field to specify if the element should be listed or not
 | 
			
		||||
    meta        = models.TextField(
 | 
			
		||||
                      _('meta')
 | 
			
		||||
                    , blank = True
 | 
			
		||||
@ -317,41 +319,52 @@ class Schedule (Model):
 | 
			
		||||
    parent      = models.ForeignKey( 'Program', blank = True, null = True )
 | 
			
		||||
    date        = models.DateTimeField(_('start'))
 | 
			
		||||
    duration    = models.TimeField(
 | 
			
		||||
                      _('duration')
 | 
			
		||||
                    , blank = True
 | 
			
		||||
                    , null = True
 | 
			
		||||
                    _('duration')
 | 
			
		||||
                  , blank = True
 | 
			
		||||
                  , null = True
 | 
			
		||||
                  )
 | 
			
		||||
    frequency   = models.SmallIntegerField(
 | 
			
		||||
                      _('frequency')
 | 
			
		||||
                    , choices = [ (y, x) for x,y in Frequency.items() ]
 | 
			
		||||
                    _('frequency')
 | 
			
		||||
                  , choices = [ (y, x) for x,y in Frequency.items() ]
 | 
			
		||||
                  )
 | 
			
		||||
    rerun       = models.ForeignKey(
 | 
			
		||||
                    'self'
 | 
			
		||||
                  , blank = True
 | 
			
		||||
                  , null = True
 | 
			
		||||
                  , help_text = "Schedule of a rerun"
 | 
			
		||||
                  )
 | 
			
		||||
    rerun       = models.BooleanField(_('rerun'), default = False)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def match_date (self, at = timezone.datetime.today(), check_time = False):
 | 
			
		||||
    def match (self, date = None, check_time = False):
 | 
			
		||||
        """
 | 
			
		||||
        Return True if the given datetime matches the schedule
 | 
			
		||||
        """
 | 
			
		||||
        if self.date.weekday() == at.weekday() and self.match_week(at):
 | 
			
		||||
            return (check_time and self.date.time() == at.date.time()) or True
 | 
			
		||||
        if not date:
 | 
			
		||||
            date = timezone.datetime.today()
 | 
			
		||||
 | 
			
		||||
        if self.date.weekday() == date.weekday() and self.match_week(date):
 | 
			
		||||
            return (check_time and self.date.time() == date.date.time()) or True
 | 
			
		||||
        return False
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def match_week (self, at = timezone.datetime.today()):
 | 
			
		||||
    def match_week (self, date = None):
 | 
			
		||||
        """
 | 
			
		||||
        Return True if the given week number matches the schedule, False
 | 
			
		||||
        otherwise.
 | 
			
		||||
        If the schedule is ponctual, return None.
 | 
			
		||||
        """
 | 
			
		||||
        if not date:
 | 
			
		||||
            date = timezone.datetime.today()
 | 
			
		||||
 | 
			
		||||
        if self.frequency == Frequency['ponctual']:
 | 
			
		||||
            return None
 | 
			
		||||
 | 
			
		||||
        if self.frequency == Frequency['one week on two']:
 | 
			
		||||
            week = at.isocalendar()[1]
 | 
			
		||||
        if self.frequency == Frequency['one on two']:
 | 
			
		||||
            week = date.isocalendar()[1]
 | 
			
		||||
            return (week % 2) == (self.date.isocalendar()[1] % 2)
 | 
			
		||||
 | 
			
		||||
        first_of_month = timezone.datetime.date(at.year, at.month, 1)
 | 
			
		||||
        week = at.isocalendar()[1] - first_of_month.isocalendar()[1]
 | 
			
		||||
        first_of_month = timezone.datetime.date(date.year, date.month, 1)
 | 
			
		||||
        week = date.isocalendar()[1] - first_of_month.isocalendar()[1]
 | 
			
		||||
 | 
			
		||||
        # weeks of month
 | 
			
		||||
        if week == 4:
 | 
			
		||||
@ -360,52 +373,62 @@ class Schedule (Model):
 | 
			
		||||
        return (self.frequency & (0b0001 << week) > 0)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def next_date (self, at = timezone.datetime.today()):
 | 
			
		||||
    def normalize (self, date):
 | 
			
		||||
        """
 | 
			
		||||
        Set the time of a datetime to the schedule's one
 | 
			
		||||
        """
 | 
			
		||||
        return date.replace( hour = self.date.hour
 | 
			
		||||
                           , minute = self.date.minute )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def dates_of_month (self, date = None):
 | 
			
		||||
        """
 | 
			
		||||
        Return a list with all matching dates of the month of the given
 | 
			
		||||
        date (= today).
 | 
			
		||||
        """
 | 
			
		||||
        if self.frequency == Frequency['ponctual']:
 | 
			
		||||
            return None
 | 
			
		||||
 | 
			
		||||
        # first day of the week
 | 
			
		||||
        date = at - timezone.timedelta( days = at.weekday() )
 | 
			
		||||
        if not date:
 | 
			
		||||
            date = timezone.datetime.today()
 | 
			
		||||
 | 
			
		||||
        # for the next five week, we look for a matching week.
 | 
			
		||||
        # when found, add the number of day since de start of the
 | 
			
		||||
        # we need to test if the result is >= at
 | 
			
		||||
        for i in range(0,5):
 | 
			
		||||
            if self.match_week(date):
 | 
			
		||||
                date_ = date + timezone.timedelta( days = self.date.weekday() )
 | 
			
		||||
                if date_ >= at:
 | 
			
		||||
                    # we don't want past events
 | 
			
		||||
                    return timezone.datetime(date_.year, date_.month, date_.day,
 | 
			
		||||
                                                self.date.hour, self.date.minute)
 | 
			
		||||
            date += timezone.timedelta( days = 7 )
 | 
			
		||||
        else:
 | 
			
		||||
            return None
 | 
			
		||||
        date = timezone.datetime( year = date.year
 | 
			
		||||
                                 , month = date.month
 | 
			
		||||
                                 , day = 1 )
 | 
			
		||||
        wday = self.date.weekday()
 | 
			
		||||
        fwday = date.weekday()
 | 
			
		||||
 | 
			
		||||
        # move date to the date weekday of the schedule
 | 
			
		||||
        # check on SO#3284452 for the formula
 | 
			
		||||
        date += timezone.timedelta(
 | 
			
		||||
                    days = (7 if fwday > wday else 0) - fwday + wday
 | 
			
		||||
                 )
 | 
			
		||||
        fwday = date.weekday()
 | 
			
		||||
 | 
			
		||||
    def next_dates (self, at = timezone.datetime.today(), n = 52):
 | 
			
		||||
        # we could have optimized this function, but since it should not
 | 
			
		||||
        # be use too often, we keep a more readable and easier to debug
 | 
			
		||||
        # solution
 | 
			
		||||
        if self.frequency == 0b000000:
 | 
			
		||||
            return None
 | 
			
		||||
        # special frequency case
 | 
			
		||||
        weeks = self.frequency
 | 
			
		||||
        if self.frequency == Frequency['last']:
 | 
			
		||||
            date += timezone.timedelta(month = 1, days = -7)
 | 
			
		||||
            return self.normalize([date])
 | 
			
		||||
        if weeks == Frequency['one on two']:
 | 
			
		||||
            # if both week are the same, then the date week of the month
 | 
			
		||||
            # matches. Note: wday % 2 + fwday % 2 => (wday + fwday) % 2
 | 
			
		||||
            fweek = date.isocalendar()[1]
 | 
			
		||||
            week = self.date.isocalendar()[1]
 | 
			
		||||
            weeks = 0b010101 if not (fweek + week) % 2 else 0b001010
 | 
			
		||||
 | 
			
		||||
        res = []
 | 
			
		||||
        for i in range(n):
 | 
			
		||||
            e = self.next_date(at)
 | 
			
		||||
            if not e:
 | 
			
		||||
                break
 | 
			
		||||
            res.append(e)
 | 
			
		||||
            at = res[-1] + timezone.timedelta(days = 1)
 | 
			
		||||
        dates = []
 | 
			
		||||
        for week in range(0,5):
 | 
			
		||||
            # NB: there can be five weeks in a month
 | 
			
		||||
            if not weeks & (0b1 << week):
 | 
			
		||||
                continue
 | 
			
		||||
 | 
			
		||||
        return res
 | 
			
		||||
            wdate = date + timezone.timedelta(days = week * 7)
 | 
			
		||||
            if wdate.month == date.month:
 | 
			
		||||
                dates.append(self.normalize(wdate))
 | 
			
		||||
        return dates
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    #def to_string(self):
 | 
			
		||||
    #    s = ugettext_lazy( RFrequency[self.frequency] )
 | 
			
		||||
    #    if self.rerun:
 | 
			
		||||
    #        return s + ' (' + _('rerun') + ')'
 | 
			
		||||
    #    return s
 | 
			
		||||
 | 
			
		||||
    def __str__ (self):
 | 
			
		||||
        frequency = [ x for x,y in Frequency.items() if y == self.frequency ]
 | 
			
		||||
        return self.parent.title + ': ' + frequency[0]
 | 
			
		||||
@ -419,19 +442,19 @@ class Schedule (Model):
 | 
			
		||||
 | 
			
		||||
class Article (Publication):
 | 
			
		||||
    parent      = models.ForeignKey(
 | 
			
		||||
                      'self'
 | 
			
		||||
                    , verbose_name = _('parent')
 | 
			
		||||
                    , blank = True
 | 
			
		||||
                    , null = True
 | 
			
		||||
                    'self'
 | 
			
		||||
                  , verbose_name = _('parent')
 | 
			
		||||
                  , blank = True
 | 
			
		||||
                  , null = True
 | 
			
		||||
                  )
 | 
			
		||||
    static_page = models.BooleanField(
 | 
			
		||||
                      _('static page')
 | 
			
		||||
                    , default = False
 | 
			
		||||
                    _('static page')
 | 
			
		||||
                  , default = False
 | 
			
		||||
                  )
 | 
			
		||||
    focus       = models.BooleanField(
 | 
			
		||||
                      _('article is focus')
 | 
			
		||||
                    , blank = True
 | 
			
		||||
                    , default = False
 | 
			
		||||
                    _('article is focus')
 | 
			
		||||
                  , blank = True
 | 
			
		||||
                  , default = False
 | 
			
		||||
                  )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -443,26 +466,26 @@ class Article (Publication):
 | 
			
		||||
 | 
			
		||||
class Program (Publication):
 | 
			
		||||
    parent      = models.ForeignKey(
 | 
			
		||||
                      Article
 | 
			
		||||
                    , verbose_name = _('parent')
 | 
			
		||||
                    , blank = True
 | 
			
		||||
                    , null = True
 | 
			
		||||
                    Article
 | 
			
		||||
                  , verbose_name = _('parent')
 | 
			
		||||
                  , blank = True
 | 
			
		||||
                  , null = True
 | 
			
		||||
                  )
 | 
			
		||||
    email       = models.EmailField(
 | 
			
		||||
                      _('email')
 | 
			
		||||
                    , max_length = 128
 | 
			
		||||
                    , null = True
 | 
			
		||||
                    , blank = True
 | 
			
		||||
                    _('email')
 | 
			
		||||
                  , max_length = 128
 | 
			
		||||
                  , null = True
 | 
			
		||||
                  , blank = True
 | 
			
		||||
                  )
 | 
			
		||||
    url         = models.URLField(
 | 
			
		||||
                    _('website')
 | 
			
		||||
                    , blank = True
 | 
			
		||||
                    , null = True
 | 
			
		||||
                  , blank = True
 | 
			
		||||
                  , null = True
 | 
			
		||||
                  )
 | 
			
		||||
    non_stop    = models.SmallIntegerField(
 | 
			
		||||
                      _('non-stop priority')
 | 
			
		||||
                    , help_text = _('this program can be used as non-stop')
 | 
			
		||||
                    , default = -1
 | 
			
		||||
                    _('non-stop priority')
 | 
			
		||||
                  , help_text = _('this program can be used as non-stop')
 | 
			
		||||
                  , default = -1
 | 
			
		||||
                  )
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
@ -498,6 +521,7 @@ class Episode (Publication):
 | 
			
		||||
    #   minimum of values.
 | 
			
		||||
    #   Duration can be retrieved from the sound file if there is one.
 | 
			
		||||
    #
 | 
			
		||||
    # FIXME: ponctual replays?
 | 
			
		||||
    parent      = models.ForeignKey(
 | 
			
		||||
                      Program
 | 
			
		||||
                    , verbose_name = _('parent')
 | 
			
		||||
@ -517,23 +541,35 @@ class Episode (Publication):
 | 
			
		||||
 | 
			
		||||
class Event (Model):
 | 
			
		||||
    """
 | 
			
		||||
    Event logs and planification of a sound file
 | 
			
		||||
    Event logs and planifications.
 | 
			
		||||
 | 
			
		||||
    An event is:
 | 
			
		||||
    - scheduled: when it has been generated following programs' Schedule
 | 
			
		||||
    - planified: when it has been generated manually/ponctually or scheduled
 | 
			
		||||
    """
 | 
			
		||||
    sound       = models.ForeignKey (
 | 
			
		||||
                      SoundFile
 | 
			
		||||
                    , verbose_name = _('sound file')
 | 
			
		||||
    parent      = models.ForeignKey (
 | 
			
		||||
                      Episode
 | 
			
		||||
                    , blank = True
 | 
			
		||||
                    , null = True
 | 
			
		||||
                  )
 | 
			
		||||
    program     = models.ForeignKey (
 | 
			
		||||
                      Program
 | 
			
		||||
                  )
 | 
			
		||||
    type        = models.SmallIntegerField(
 | 
			
		||||
                      _('type')
 | 
			
		||||
                    , choices = [ (y, x) for x,y in EventType.items() ]
 | 
			
		||||
                  )
 | 
			
		||||
    date        = models.DateTimeField( _('date of event start') )
 | 
			
		||||
    meta        = models.TextField (
 | 
			
		||||
                      _('meta')
 | 
			
		||||
                    , blank = True
 | 
			
		||||
                    , null = True
 | 
			
		||||
    stream      = models.SmallIntegerField(
 | 
			
		||||
                      _('stream')
 | 
			
		||||
                    , default = 0
 | 
			
		||||
                    , help_text = 'stream id on which the event happens'
 | 
			
		||||
                  )
 | 
			
		||||
    scheduled   = models.BooleanField(
 | 
			
		||||
                      _('automated')
 | 
			
		||||
                    , default = False
 | 
			
		||||
                    , help_text = 'event generated automatically'
 | 
			
		||||
                  )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        verbose_name = _('Event')
 | 
			
		||||
 | 
			
		||||
@ -6,11 +6,19 @@ def ensure (key, default):
 | 
			
		||||
    globals()[key] = getattr(settings, key, default)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Directory for the programs data
 | 
			
		||||
ensure('AIRCOX_PROGRAMS_DIR',
 | 
			
		||||
       os.path.join(settings.MEDIA_ROOT, 'programs'))
 | 
			
		||||
 | 
			
		||||
# Default directory for the soundfiles
 | 
			
		||||
ensure('AIRCOX_SOUNDFILE_DEFAULT_DIR',
 | 
			
		||||
       os.path.join(AIRCOX_PROGRAMS_DIR + 'default'))
 | 
			
		||||
 | 
			
		||||
# Extension of sound files
 | 
			
		||||
ensure('AIRCOX_SOUNDFILE_EXT',
 | 
			
		||||
        ('ogg','flac','wav','mp3','opus'))
 | 
			
		||||
 | 
			
		||||
# Stream for the scheduled events
 | 
			
		||||
ensure('AIRCOX_SCHEDULED_STREAM', 0)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,43 +1,52 @@
 | 
			
		||||
from django.utils                   import timezone
 | 
			
		||||
 | 
			
		||||
from programs.models                import Schedule, Event, Episode,\
 | 
			
		||||
                                           SoundFile, Frequency
 | 
			
		||||
                                           EventType
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def update_scheduled_events (date):
 | 
			
		||||
def scheduled_month_events (date = None, unsaved_only = False):
 | 
			
		||||
    """
 | 
			
		||||
    Update planified events from schedules
 | 
			
		||||
    TODO: notification in case of conflicts?
 | 
			
		||||
    Return a list of scheduled events for the month of the given date. For the
 | 
			
		||||
    non existing events, a program attribute to the corresponding program is
 | 
			
		||||
    set.
 | 
			
		||||
    """
 | 
			
		||||
    all_schedules = Schedule.objects().all()
 | 
			
		||||
    schedules = [ schedule
 | 
			
		||||
                    for schedule in models.Schedule.objects().all()
 | 
			
		||||
                    if schedule.match-date(date) ]
 | 
			
		||||
    if not date:
 | 
			
		||||
        date = timezone.datetime.today()
 | 
			
		||||
 | 
			
		||||
    schedules.sort(key = lambda e: e.date)
 | 
			
		||||
    schedules = Schedule.objects.all()
 | 
			
		||||
    events = []
 | 
			
		||||
 | 
			
		||||
    for schedule in schedules:
 | 
			
		||||
        if schedule.frequency == Frequency['ponctual']:
 | 
			
		||||
            continue
 | 
			
		||||
        dates = schedule.dates_of_month()
 | 
			
		||||
        for date in dates:
 | 
			
		||||
            event = Event.objects \
 | 
			
		||||
                         .filter(date = date, parent__parent = schedule.parent)
 | 
			
		||||
 | 
			
		||||
        ev_date = timezone.datetime(date.year, date.month, date.day,
 | 
			
		||||
                                    schedule.date.hour, schedule.date.minute)
 | 
			
		||||
            if event.count():
 | 
			
		||||
                if not unsaved_only:
 | 
			
		||||
                    events.append(event)
 | 
			
		||||
                continue
 | 
			
		||||
 | 
			
		||||
        # if event exists, pass
 | 
			
		||||
        n = Event.objects() \
 | 
			
		||||
                 .filter(date = ev_date, parent__parent = schedule.parent) \
 | 
			
		||||
                 .count()
 | 
			
		||||
        if n:
 | 
			
		||||
            continue
 | 
			
		||||
            # get episode
 | 
			
		||||
            ep_date = date
 | 
			
		||||
            if schedule.rerun:
 | 
			
		||||
                ep_date = schedule.rerun.date
 | 
			
		||||
 | 
			
		||||
        ep_date = ev_date
 | 
			
		||||
            episode = Episode.objects().filter( date = ep_date
 | 
			
		||||
                                              , parent = schedule.parent )
 | 
			
		||||
            episode  = episode[0] if episode.count() else None
 | 
			
		||||
 | 
			
		||||
        # rerun?
 | 
			
		||||
        if schedule.rerun:
 | 
			
		||||
            schedule = schedule.rerun
 | 
			
		||||
            date_ = schedule.date
 | 
			
		||||
 | 
			
		||||
        episode = Episode.objects().filter(date = date)
 | 
			
		||||
            # make event
 | 
			
		||||
            event = Event( parent = episode
 | 
			
		||||
                         , program = schedule.parent
 | 
			
		||||
                         , type = EventType['diffuse']
 | 
			
		||||
                         , date = date
 | 
			
		||||
                         , stream = settings.AIRCOX_SCHEDULED_STREAM
 | 
			
		||||
                         , scheduled = True
 | 
			
		||||
                         )
 | 
			
		||||
            event.program = schedule.program
 | 
			
		||||
            events.append(event)
 | 
			
		||||
    return events
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user