code style + add non_stop field to episode
This commit is contained in:
		@ -3,7 +3,8 @@ Platform to manage radio programs, schedules, cms, etc. -- main test repo
 | 
			
		||||
 | 
			
		||||
# Applications
 | 
			
		||||
* **programs**: core application that have all defined models
 | 
			
		||||
* **monitor**:  monitor file system for new podcasts and sounds
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Note
 | 
			
		||||
We make the assumption that admin is used with autocomplete-light and django-suit
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -22,16 +22,16 @@ import programs.settings                    as settings
 | 
			
		||||
# Important: the first week is always the first week where the weekday of the
 | 
			
		||||
# schedule is present.
 | 
			
		||||
Frequency = {
 | 
			
		||||
    'ponctual':         0b000000
 | 
			
		||||
  , 'first':            0b000001
 | 
			
		||||
  , 'second':           0b000010
 | 
			
		||||
  , 'third':            0b000100
 | 
			
		||||
  , 'fourth':           0b001000
 | 
			
		||||
  , 'last':             0b010000
 | 
			
		||||
  , 'first and third':  0b000101
 | 
			
		||||
  , 'second and fourth': 0b001010
 | 
			
		||||
  , 'every':            0b011111
 | 
			
		||||
  , 'one on two':       0b100000
 | 
			
		||||
    'ponctual':         0b000000,
 | 
			
		||||
    'first':            0b000001,
 | 
			
		||||
    'second':           0b000010,
 | 
			
		||||
    'third':            0b000100,
 | 
			
		||||
    'fourth':           0b001000,
 | 
			
		||||
    'last':             0b010000,
 | 
			
		||||
    'first and third':  0b000101,
 | 
			
		||||
    'second and fourth': 0b001010,
 | 
			
		||||
    'every':            0b011111,
 | 
			
		||||
    'one on two':       0b100000,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -49,10 +49,10 @@ ugettext_lazy('one on two')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
DiffusionType = {
 | 
			
		||||
    'diffuse':   0x01   # the diffusion is planified or done
 | 
			
		||||
  , 'scheduled': 0x02   # the diffusion been scheduled automatically
 | 
			
		||||
  , 'cancel':    0x03   # the diffusion has been canceled from grid; useful to give
 | 
			
		||||
                        # the info to the users
 | 
			
		||||
    'diffuse': 0x01,    # the diffusion is planified or done
 | 
			
		||||
    'scheduled': 0x02,  # the diffusion has been scheduled automatically
 | 
			
		||||
    'cancel': 0x03,     # the diffusion has been canceled from grid; useful to
 | 
			
		||||
                        # give the info to the users
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -73,7 +73,6 @@ class Model (models.Model):
 | 
			
		||||
        """
 | 
			
		||||
        return cl.type() + 's'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def name (cl, plural = False):
 | 
			
		||||
        """
 | 
			
		||||
@ -83,77 +82,89 @@ class Model (models.Model):
 | 
			
		||||
            return cl._meta.verbose_name_plural.title()
 | 
			
		||||
        return cl._meta.verbose_name.title()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        abstract = True
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Metadata (Model):
 | 
			
		||||
    """
 | 
			
		||||
    meta is used to extend a model for future needs
 | 
			
		||||
    """
 | 
			
		||||
    author      = models.ForeignKey (
 | 
			
		||||
                      User
 | 
			
		||||
                    , verbose_name = _('author')
 | 
			
		||||
                    , blank = True
 | 
			
		||||
                    , null = True
 | 
			
		||||
                  )
 | 
			
		||||
    title       = models.CharField(
 | 
			
		||||
                      _('title')
 | 
			
		||||
                    , max_length = 128
 | 
			
		||||
                  )
 | 
			
		||||
    date        = models.DateTimeField(
 | 
			
		||||
                      _('date')
 | 
			
		||||
                    , default = timezone.datetime.now
 | 
			
		||||
                  )
 | 
			
		||||
    public      = models.BooleanField(
 | 
			
		||||
                      _('public')
 | 
			
		||||
                    , default = True
 | 
			
		||||
                    , help_text = _('publication is public')
 | 
			
		||||
                  )
 | 
			
		||||
    enumerable  = models.BooleanField(
 | 
			
		||||
                      _('enumerable')
 | 
			
		||||
                    , default = True
 | 
			
		||||
                    , help_text = _('publication is listable')
 | 
			
		||||
                  )
 | 
			
		||||
    tags        = TaggableManager(
 | 
			
		||||
                      _('tags')
 | 
			
		||||
                    , blank = True
 | 
			
		||||
                  )
 | 
			
		||||
    author = models.ForeignKey (
 | 
			
		||||
        User,
 | 
			
		||||
        verbose_name = _('author'),
 | 
			
		||||
        blank = True,
 | 
			
		||||
        null = True,
 | 
			
		||||
    )
 | 
			
		||||
    title = models.CharField(
 | 
			
		||||
        _('title'),
 | 
			
		||||
        max_length = 128,
 | 
			
		||||
    )
 | 
			
		||||
    date = models.DateTimeField(
 | 
			
		||||
        _('date'),
 | 
			
		||||
        default = timezone.datetime.now,
 | 
			
		||||
    )
 | 
			
		||||
    public = models.BooleanField(
 | 
			
		||||
        _('public'),
 | 
			
		||||
        default = True,
 | 
			
		||||
        help_text = _('publication is public'),
 | 
			
		||||
    )
 | 
			
		||||
    enumerable = models.BooleanField(
 | 
			
		||||
        _('enumerable'),
 | 
			
		||||
        default = True,
 | 
			
		||||
        help_text = _('publication is listable'),
 | 
			
		||||
    )
 | 
			
		||||
    tags = TaggableManager(
 | 
			
		||||
        _('tags'),
 | 
			
		||||
        blank = True,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        abstract = True
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Publication (Metadata):
 | 
			
		||||
    subtitle = models.CharField(
 | 
			
		||||
        _('subtitle'),
 | 
			
		||||
        max_length = 128,
 | 
			
		||||
        blank = True,
 | 
			
		||||
    )
 | 
			
		||||
    img = models.ImageField(
 | 
			
		||||
        _('image'),
 | 
			
		||||
        upload_to = "images",
 | 
			
		||||
        blank = True,
 | 
			
		||||
    )
 | 
			
		||||
    content = models.TextField(
 | 
			
		||||
        _('content'),
 | 
			
		||||
        blank = True,
 | 
			
		||||
    )
 | 
			
		||||
    commentable = models.BooleanField(
 | 
			
		||||
        _('enable comments'),
 | 
			
		||||
        default = True,
 | 
			
		||||
        help_text = _('comments are enabled on this publication'),
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    def get_slug_name (self):
 | 
			
		||||
        return slugify(self.title)
 | 
			
		||||
 | 
			
		||||
    def __str__ (self):
 | 
			
		||||
        return self.title + ' (' + str(self.id) + ')'
 | 
			
		||||
 | 
			
		||||
    subtitle    = models.CharField(
 | 
			
		||||
                      _('subtitle')
 | 
			
		||||
                    , max_length = 128
 | 
			
		||||
                    , blank = True
 | 
			
		||||
                  )
 | 
			
		||||
    img         = models.ImageField(
 | 
			
		||||
                      _('image')
 | 
			
		||||
                    , upload_to = "images"
 | 
			
		||||
                    , blank = True
 | 
			
		||||
                  )
 | 
			
		||||
    content     = models.TextField(
 | 
			
		||||
                      _('content')
 | 
			
		||||
                    , blank = True
 | 
			
		||||
                  )
 | 
			
		||||
    commentable = models.BooleanField(
 | 
			
		||||
                      _('enable comments')
 | 
			
		||||
                    , default = True
 | 
			
		||||
                    , help_text = _('comments are enabled on this publication')
 | 
			
		||||
                  )
 | 
			
		||||
    def get_parents (self, order_by = "desc", include_fields = None):
 | 
			
		||||
        """
 | 
			
		||||
        Return an array of the parents of the item.
 | 
			
		||||
        If include_fields is an array of files to include.
 | 
			
		||||
        """
 | 
			
		||||
        # TODO: fields included
 | 
			
		||||
        # FIXME: parameter name + container
 | 
			
		||||
        parents = [ self ]
 | 
			
		||||
        while parents[-1].parent:
 | 
			
		||||
            parent = parents[-1].parent
 | 
			
		||||
            if parent not in parents:
 | 
			
		||||
                # avoid cycles
 | 
			
		||||
                parents.append(parent)
 | 
			
		||||
        parents = parents[1:]
 | 
			
		||||
 | 
			
		||||
        if order_by == 'desc':
 | 
			
		||||
            return reversed(parents)
 | 
			
		||||
        return parents
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def _exclude_args (allow_unpublished = False, prefix = ''):
 | 
			
		||||
@ -165,7 +176,6 @@ class Publication (Metadata):
 | 
			
		||||
        res[prefix + 'date__gt'] = timezone.now()
 | 
			
		||||
        return res
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def get_available (cl, first = False, **kwargs):
 | 
			
		||||
        """
 | 
			
		||||
@ -183,70 +193,46 @@ class Publication (Metadata):
 | 
			
		||||
            return (e and e[0]) or None
 | 
			
		||||
        return e or None
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def get_parents (self, order_by = "desc", include_fields = None):
 | 
			
		||||
        """
 | 
			
		||||
        Return an array of the parents of the item.
 | 
			
		||||
        If include_fields is an array of files to include.
 | 
			
		||||
        """
 | 
			
		||||
        # TODO: fields included
 | 
			
		||||
        # FIXME: parameter name + container
 | 
			
		||||
        parents = [ self ]
 | 
			
		||||
        while parents[-1].parent:
 | 
			
		||||
            parent = parents[-1].parent
 | 
			
		||||
            if parent not in parents:
 | 
			
		||||
                # avoid cycles
 | 
			
		||||
                parents.append(parent)
 | 
			
		||||
 | 
			
		||||
        parents = parents[1:]
 | 
			
		||||
 | 
			
		||||
        if order_by == 'desc':
 | 
			
		||||
            return reversed(parents)
 | 
			
		||||
        return parents
 | 
			
		||||
 | 
			
		||||
    def __str__ (self):
 | 
			
		||||
        return self.title + ' (' + str(self.id) + ')'
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        abstract = True
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# Usable models
 | 
			
		||||
#
 | 
			
		||||
class Track (Model):
 | 
			
		||||
    # There are no nice solution for M2M relations ship (even without
 | 
			
		||||
    # through) in django-admin. So we unfortunately need to make one-
 | 
			
		||||
    # to-one relations and add a position argument
 | 
			
		||||
    episode     = models.ForeignKey(
 | 
			
		||||
                      'Episode'
 | 
			
		||||
                    , null = True
 | 
			
		||||
                  )
 | 
			
		||||
    artist      = models.CharField(
 | 
			
		||||
                      _('artist')
 | 
			
		||||
                    , max_length = 128
 | 
			
		||||
                  )
 | 
			
		||||
    title       = models.CharField(
 | 
			
		||||
                      _('title')
 | 
			
		||||
                    , max_length = 128
 | 
			
		||||
                  )
 | 
			
		||||
    episode = models.ForeignKey(
 | 
			
		||||
        'Episode',
 | 
			
		||||
        null = True,
 | 
			
		||||
    )
 | 
			
		||||
    artist = models.CharField(
 | 
			
		||||
        _('artist'),
 | 
			
		||||
        max_length = 128,
 | 
			
		||||
    )
 | 
			
		||||
    title = models.CharField(
 | 
			
		||||
        _('title'),
 | 
			
		||||
        max_length = 128,
 | 
			
		||||
    )
 | 
			
		||||
    tags        = TaggableManager( blank = True )
 | 
			
		||||
    # position can be used to specify a position in seconds
 | 
			
		||||
    # position can be used to specify a position in seconds for non-stop
 | 
			
		||||
    # programs
 | 
			
		||||
    position    = models.SmallIntegerField(
 | 
			
		||||
                      default = 0
 | 
			
		||||
                    , help_text=_('position in the playlist')
 | 
			
		||||
                  )
 | 
			
		||||
 | 
			
		||||
        default = 0,
 | 
			
		||||
        help_text=_('position in the playlist'),
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        return ' '.join([self.artist, ':', self.title])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        verbose_name = _('Track')
 | 
			
		||||
        verbose_name_plural = _('Tracks')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Sound (Metadata):
 | 
			
		||||
    """
 | 
			
		||||
    A Sound is the representation of a sound, that can be:
 | 
			
		||||
@ -261,44 +247,34 @@ class Sound (Metadata):
 | 
			
		||||
    Each sound file can be associated to a filesystem's file or an embedded
 | 
			
		||||
    code (for external podcasts).
 | 
			
		||||
    """
 | 
			
		||||
    def get_upload_path (self, filename):
 | 
			
		||||
        if self.parent and self.parent.parent:
 | 
			
		||||
            path = self.parent.parent.path
 | 
			
		||||
        else:
 | 
			
		||||
            path = settings.AIRCOX_SOUNDFILE_DEFAULT_DIR
 | 
			
		||||
        return os.path.join(path, filename)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    path        = models.FilePathField( #FIXME: filefield
 | 
			
		||||
                      _('file')
 | 
			
		||||
                    , path = settings.AIRCOX_PROGRAMS_DIR
 | 
			
		||||
                    , match = '*(' \
 | 
			
		||||
                            + '|'.join(settings.AIRCOX_SOUNDFILE_EXT) + ')$'
 | 
			
		||||
                    , recursive = True
 | 
			
		||||
                    , blank = True
 | 
			
		||||
                    , null = True
 | 
			
		||||
                  )
 | 
			
		||||
    duration    = models.TimeField(
 | 
			
		||||
                      _('duration')
 | 
			
		||||
                    , blank = True
 | 
			
		||||
                    , null = True
 | 
			
		||||
                  )
 | 
			
		||||
    fragment    = models.BooleanField(
 | 
			
		||||
                      _('incomplete sound')
 | 
			
		||||
                    , default = False
 | 
			
		||||
                    , help_text = _("the file has been cut")
 | 
			
		||||
                  )
 | 
			
		||||
    embed       = models.TextField(
 | 
			
		||||
                      _('embed HTML code from external website')
 | 
			
		||||
                    , blank = True
 | 
			
		||||
                    , null = True
 | 
			
		||||
                    , help_text = _('if set, consider the sound podcastable from there')
 | 
			
		||||
                  )
 | 
			
		||||
    removed     = models.BooleanField(
 | 
			
		||||
                      default = False
 | 
			
		||||
                    , help_text = _('this sound has been removed from filesystem')
 | 
			
		||||
                  )
 | 
			
		||||
 | 
			
		||||
    path = models.FilePathField(
 | 
			
		||||
        _('file'),
 | 
			
		||||
        path = settings.AIRCOX_PROGRAMS_DIR,
 | 
			
		||||
        match = '*(' + '|'.join(settings.AIRCOX_SOUNDFILE_EXT) + ')$',
 | 
			
		||||
        recursive = True,
 | 
			
		||||
        blank = True,
 | 
			
		||||
        null = True,
 | 
			
		||||
    )
 | 
			
		||||
    duration = models.TimeField(
 | 
			
		||||
        _('duration'),
 | 
			
		||||
        blank = True,
 | 
			
		||||
        null = True,
 | 
			
		||||
    )
 | 
			
		||||
    fragment = models.BooleanField(
 | 
			
		||||
        _('incomplete sound'),
 | 
			
		||||
        default = False,
 | 
			
		||||
        help_text = _("the file has been cut"),
 | 
			
		||||
    )
 | 
			
		||||
    embed = models.TextField(
 | 
			
		||||
        _('embed HTML code from external website'),
 | 
			
		||||
        blank = True,
 | 
			
		||||
        null = True,
 | 
			
		||||
        help_text = _('if set, consider the sound podcastable'),
 | 
			
		||||
    )
 | 
			
		||||
    removed = models.BooleanField(
 | 
			
		||||
        default = False,
 | 
			
		||||
        help_text = _('this sound has been removed from filesystem'),
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    def get_mtime (self):
 | 
			
		||||
        """
 | 
			
		||||
@ -308,47 +284,41 @@ class Sound (Metadata):
 | 
			
		||||
        mtime = timezone.datetime.fromtimestamp(mtime)
 | 
			
		||||
        return timezone.make_aware(mtime, timezone.get_current_timezone())
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def save (self, *args, **kwargs):
 | 
			
		||||
        if not self.pk:
 | 
			
		||||
            self.date = self.get_mtime()
 | 
			
		||||
        super(Sound, self).save(*args, **kwargs)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def autocomplete_search_fields():
 | 
			
		||||
        return ("id__iexact", "path__icontains", 'title__icontains')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def __str__ (self):
 | 
			
		||||
        return '/'.join(self.path.split('/')[-3:])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        verbose_name = _('Sound')
 | 
			
		||||
        verbose_name_plural = _('Sounds')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Schedule (Model):
 | 
			
		||||
    parent      = models.ForeignKey( 'Program', blank = True, null = True )
 | 
			
		||||
    date        = models.DateTimeField(_('start'))
 | 
			
		||||
    duration    = models.TimeField(
 | 
			
		||||
                    _('duration')
 | 
			
		||||
                  , blank = True
 | 
			
		||||
                  , null = True
 | 
			
		||||
                  )
 | 
			
		||||
    frequency   = models.SmallIntegerField(
 | 
			
		||||
                    _('frequency')
 | 
			
		||||
                  , choices = [ (y, x) for x,y in Frequency.items() ]
 | 
			
		||||
                  )
 | 
			
		||||
    rerun       = models.ForeignKey(
 | 
			
		||||
                    'self'
 | 
			
		||||
                  , blank = True
 | 
			
		||||
                  , null = True
 | 
			
		||||
                  , help_text = "Schedule of a rerun"
 | 
			
		||||
                  )
 | 
			
		||||
 | 
			
		||||
    parent = models.ForeignKey(
 | 
			
		||||
        'Program',
 | 
			
		||||
        blank = True,
 | 
			
		||||
        null = True,
 | 
			
		||||
    )
 | 
			
		||||
    begin = models.DateTimeField(_('begin'))
 | 
			
		||||
    end = models.DateTimeField(
 | 
			
		||||
        _('end'),
 | 
			
		||||
        blank = True,
 | 
			
		||||
        null = True,
 | 
			
		||||
    )
 | 
			
		||||
    frequency = models.SmallIntegerField(
 | 
			
		||||
        _('frequency'),
 | 
			
		||||
        choices = [ (y, x) for x,y in Frequency.items() ],
 | 
			
		||||
    )
 | 
			
		||||
    rerun = models.ForeignKey(
 | 
			
		||||
        'self',
 | 
			
		||||
        blank = True,
 | 
			
		||||
        null = True,
 | 
			
		||||
        help_text = "Schedule of a rerun",
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    def match (self, date = None, check_time = False):
 | 
			
		||||
        """
 | 
			
		||||
@ -358,10 +328,10 @@ class Schedule (Model):
 | 
			
		||||
            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 (check_time and self.date.time() == date.date.time()) \
 | 
			
		||||
                    or True
 | 
			
		||||
        return False
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def match_week (self, date = None):
 | 
			
		||||
        """
 | 
			
		||||
        Return True if the given week number matches the schedule, False
 | 
			
		||||
@ -387,14 +357,12 @@ class Schedule (Model):
 | 
			
		||||
            return self.frequency == 0b1111
 | 
			
		||||
        return (self.frequency & (0b0001 << week) > 0)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    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 )
 | 
			
		||||
 | 
			
		||||
        return date.replace(hour = self.date.hour,
 | 
			
		||||
                            minute = self.date.minute)
 | 
			
		||||
 | 
			
		||||
    def dates_of_month (self, date = None):
 | 
			
		||||
        """
 | 
			
		||||
@ -407,9 +375,9 @@ class Schedule (Model):
 | 
			
		||||
        if not date:
 | 
			
		||||
            date = timezone.datetime.today()
 | 
			
		||||
 | 
			
		||||
        date = timezone.datetime( year = date.year
 | 
			
		||||
                                 , month = date.month
 | 
			
		||||
                                 , day = 1 )
 | 
			
		||||
        date = timezone.datetime(year = date.year,
 | 
			
		||||
                                 month = date.month,
 | 
			
		||||
                                 day = 1)
 | 
			
		||||
        wday = self.date.weekday()
 | 
			
		||||
        fwday = date.weekday()
 | 
			
		||||
 | 
			
		||||
@ -417,7 +385,7 @@ class Schedule (Model):
 | 
			
		||||
        # check on SO#3284452 for the formula
 | 
			
		||||
        date += timezone.timedelta(
 | 
			
		||||
                    days = (7 if fwday > wday else 0) - fwday + wday
 | 
			
		||||
                 )
 | 
			
		||||
                )
 | 
			
		||||
        fwday = date.weekday()
 | 
			
		||||
 | 
			
		||||
        # special frequency case
 | 
			
		||||
@ -443,7 +411,6 @@ class Schedule (Model):
 | 
			
		||||
                dates.append(self.normalize(wdate))
 | 
			
		||||
        return dates
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def diffusions_of_month (self, date = None, exclude_saved = False):
 | 
			
		||||
        """
 | 
			
		||||
        Return a list of generated (unsaved) diffusions for this program for the
 | 
			
		||||
@ -456,11 +423,10 @@ class Schedule (Model):
 | 
			
		||||
        if not date:
 | 
			
		||||
            date = timezone.datetime.today()
 | 
			
		||||
 | 
			
		||||
        diffusions = []
 | 
			
		||||
 | 
			
		||||
        dates = self.dates_of_month()
 | 
			
		||||
        saved = Diffusion.objects.filter( date__in = dates
 | 
			
		||||
                                        , program = self.parent )
 | 
			
		||||
        saved = Diffusion.objects.filter(date__in = dates,
 | 
			
		||||
                                         program = self.parent)
 | 
			
		||||
        diffusions = []
 | 
			
		||||
 | 
			
		||||
        # existing diffusions
 | 
			
		||||
        for saved_item in saved:
 | 
			
		||||
@ -475,124 +441,116 @@ class Schedule (Model):
 | 
			
		||||
            if self.rerun:
 | 
			
		||||
                ep_date = self.rerun.date
 | 
			
		||||
 | 
			
		||||
            episode = Episode.objects().filter( date = ep_date
 | 
			
		||||
                                              , parent = self.parent )
 | 
			
		||||
            episode = Episode.objects().filter(date = ep_date,
 | 
			
		||||
                                               parent = self.parent)
 | 
			
		||||
            episode  = episode[0] if episode.count() else None
 | 
			
		||||
 | 
			
		||||
            # make diffusion
 | 
			
		||||
            diffusion = Diffusion( parent = episode
 | 
			
		||||
                         , program = self.parent
 | 
			
		||||
                         , type = DiffusionType['diffuse']
 | 
			
		||||
                         , date = date
 | 
			
		||||
                         , stream = settings.AIRCOX_SCHEDULED_STREAM
 | 
			
		||||
                         , selfd = True
 | 
			
		||||
                         )
 | 
			
		||||
            diffusion = Diffusion(
 | 
			
		||||
                            episode = episode,
 | 
			
		||||
                            program = self.parent,
 | 
			
		||||
                            type = DiffusionType['scheduled'],
 | 
			
		||||
                            begin = date,
 | 
			
		||||
                            end = timezone.datetime.combine(date.date(),
 | 
			
		||||
                                                            self.end.time()),
 | 
			
		||||
                            stream = settings.AIRCOX_SCHEDULED_STREAM,
 | 
			
		||||
                        )
 | 
			
		||||
            diffusion.program = self.program
 | 
			
		||||
            diffusions.append(diffusion)
 | 
			
		||||
        return diffusions
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def __str__ (self):
 | 
			
		||||
        frequency = [ x for x,y in Frequency.items() if y == self.frequency ]
 | 
			
		||||
        return self.parent.title + ': ' + frequency[0]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        verbose_name = _('Schedule')
 | 
			
		||||
        verbose_name_plural = _('Schedules')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Article (Publication):
 | 
			
		||||
    parent      = models.ForeignKey(
 | 
			
		||||
                    'self'
 | 
			
		||||
                  , verbose_name = _('parent')
 | 
			
		||||
                  , blank = True
 | 
			
		||||
                  , null = True
 | 
			
		||||
                  , help_text = _('parent article')
 | 
			
		||||
                  )
 | 
			
		||||
    parent = models.ForeignKey(
 | 
			
		||||
        'self',
 | 
			
		||||
        verbose_name = _('parent'),
 | 
			
		||||
        blank = True,
 | 
			
		||||
        null = True,
 | 
			
		||||
        help_text = _('parent article'),
 | 
			
		||||
    )
 | 
			
		||||
    static_page = models.BooleanField(
 | 
			
		||||
                    _('static page')
 | 
			
		||||
                  , default = False
 | 
			
		||||
                  )
 | 
			
		||||
    focus       = models.BooleanField(
 | 
			
		||||
                    _('article is focus')
 | 
			
		||||
                  , blank = True
 | 
			
		||||
                  , default = False
 | 
			
		||||
                  )
 | 
			
		||||
 | 
			
		||||
        _('static page'),
 | 
			
		||||
        default = False,
 | 
			
		||||
    )
 | 
			
		||||
    focus = models.BooleanField(
 | 
			
		||||
        _('article is focus'),
 | 
			
		||||
        blank = True,
 | 
			
		||||
        default = False,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        verbose_name = _('Article')
 | 
			
		||||
        verbose_name_plural = _('Articles')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Program (Publication):
 | 
			
		||||
    parent      = models.ForeignKey(
 | 
			
		||||
                    Article
 | 
			
		||||
                  , verbose_name = _('parent')
 | 
			
		||||
                  , blank = True
 | 
			
		||||
                  , null = True
 | 
			
		||||
                  , help_text = _('parent article')
 | 
			
		||||
                  )
 | 
			
		||||
 | 
			
		||||
    email       = models.EmailField(
 | 
			
		||||
                    _('email')
 | 
			
		||||
                  , max_length = 128
 | 
			
		||||
                  , null = True
 | 
			
		||||
                  , blank = True
 | 
			
		||||
                  )
 | 
			
		||||
 | 
			
		||||
    url         = models.URLField(
 | 
			
		||||
                    _('website')
 | 
			
		||||
                  , blank = True
 | 
			
		||||
                  , null = True
 | 
			
		||||
                  )
 | 
			
		||||
    parent = models.ForeignKey(
 | 
			
		||||
        Article,
 | 
			
		||||
        verbose_name = _('parent'),
 | 
			
		||||
        blank = True,
 | 
			
		||||
        null = True,
 | 
			
		||||
        help_text = _('parent article'),
 | 
			
		||||
    )
 | 
			
		||||
    email = models.EmailField(
 | 
			
		||||
        _('email'),
 | 
			
		||||
        max_length = 128,
 | 
			
		||||
        null = True,
 | 
			
		||||
        blank = True,
 | 
			
		||||
    )
 | 
			
		||||
    url = models.URLField(
 | 
			
		||||
        _('website'),
 | 
			
		||||
        blank = True,
 | 
			
		||||
        null = True,
 | 
			
		||||
    )
 | 
			
		||||
    non_stop = models.BooleanField(
 | 
			
		||||
        _('non-stop'),
 | 
			
		||||
        default = False,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def path (self):
 | 
			
		||||
        return os.path.join( settings.AIRCOX_PROGRAMS_DIR
 | 
			
		||||
                           , slugify(self.title + '_' + str(self.id))
 | 
			
		||||
                           )
 | 
			
		||||
 | 
			
		||||
        return os.path.join(settings.AIRCOX_PROGRAMS_DIR,
 | 
			
		||||
                            slugify(self.title + '_' + str(self.id)) )
 | 
			
		||||
 | 
			
		||||
    def find_schedule (self, date):
 | 
			
		||||
        """
 | 
			
		||||
        Return the first schedule that matches a given date
 | 
			
		||||
        """
 | 
			
		||||
        print(self)
 | 
			
		||||
        schedules = Schedule.objects.filter(parent = self)
 | 
			
		||||
        for schedule in schedules:
 | 
			
		||||
            if schedule.match(date):
 | 
			
		||||
                return schedule
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        verbose_name = _('Program')
 | 
			
		||||
        verbose_name_plural = _('Programs')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Episode (Publication):
 | 
			
		||||
    # Note:
 | 
			
		||||
    #   We do not especially need a duration here, because even if an
 | 
			
		||||
    #   emussion's schedule can have specified durations, in practice this
 | 
			
		||||
    #   program's schedule can have specified durations, in practice this
 | 
			
		||||
    #   duration may vary. Furthermore, we want the users have to enter a
 | 
			
		||||
    #   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')
 | 
			
		||||
                    , help_text = _('parent program')
 | 
			
		||||
                  )
 | 
			
		||||
    sounds      = models.ManyToManyField(
 | 
			
		||||
                      Sound
 | 
			
		||||
                    , blank = True
 | 
			
		||||
                    , verbose_name = _('sounds')
 | 
			
		||||
                  )
 | 
			
		||||
    parent = models.ForeignKey(
 | 
			
		||||
        Program,
 | 
			
		||||
        verbose_name = _('parent'),
 | 
			
		||||
        help_text = _('parent program'),
 | 
			
		||||
    )
 | 
			
		||||
    sounds = models.ManyToManyField(
 | 
			
		||||
        Sound,
 | 
			
		||||
        blank = True,
 | 
			
		||||
        verbose_name = _('sounds'),
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        verbose_name = _('Episode')
 | 
			
		||||
@ -608,28 +566,31 @@ class Diffusion (Model):
 | 
			
		||||
    - scheduled: when it has been generated following programs' Schedule
 | 
			
		||||
    - planified: when it has been generated manually/ponctually or scheduled
 | 
			
		||||
    """
 | 
			
		||||
    episode     = models.ForeignKey (
 | 
			
		||||
                      Episode
 | 
			
		||||
                    , blank = True
 | 
			
		||||
                    , null = True
 | 
			
		||||
                    , verbose_name = _('episode')
 | 
			
		||||
                  )
 | 
			
		||||
    program     = models.ForeignKey (
 | 
			
		||||
                      Program
 | 
			
		||||
                    , verbose_name = _('program')
 | 
			
		||||
                  )
 | 
			
		||||
    type        = models.SmallIntegerField(
 | 
			
		||||
                      verbose_name = _('type')
 | 
			
		||||
                    , choices = [ (y, x) for x,y in DiffusionType.items() ]
 | 
			
		||||
                  )
 | 
			
		||||
    begin       = models.DateTimeField( _('start of diffusion start') )
 | 
			
		||||
    end         = models.DateTimeField( _('stop of diffusion stop') )
 | 
			
		||||
    stream      = models.SmallIntegerField(
 | 
			
		||||
                      verbose_name = _('stream')
 | 
			
		||||
                    , default = 0
 | 
			
		||||
                    , help_text = 'stream id on which the diffusion happens'
 | 
			
		||||
                  )
 | 
			
		||||
 | 
			
		||||
    episode = models.ForeignKey (
 | 
			
		||||
        Episode,
 | 
			
		||||
        blank = True,
 | 
			
		||||
        null = True,
 | 
			
		||||
        verbose_name = _('episode'),
 | 
			
		||||
    )
 | 
			
		||||
    program = models.ForeignKey (
 | 
			
		||||
        Program,
 | 
			
		||||
        verbose_name = _('program'),
 | 
			
		||||
    )
 | 
			
		||||
    type = models.SmallIntegerField(
 | 
			
		||||
        verbose_name = _('type'),
 | 
			
		||||
        choices = [ (y, x) for x,y in DiffusionType.items() ],
 | 
			
		||||
    )
 | 
			
		||||
    begin = models.DateTimeField( _('start of the diffusion') )
 | 
			
		||||
    end = models.DateTimeField(
 | 
			
		||||
        _('end of the diffusion'),
 | 
			
		||||
        blank = True,
 | 
			
		||||
        null = True,
 | 
			
		||||
    )
 | 
			
		||||
    stream = models.SmallIntegerField(
 | 
			
		||||
        verbose_name = _('stream'),
 | 
			
		||||
        default = 0,
 | 
			
		||||
        help_text = 'stream id on which the diffusion happens',
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    def save (self, *args, **kwargs):
 | 
			
		||||
        if self.episode:
 | 
			
		||||
@ -640,7 +601,6 @@ class Diffusion (Model):
 | 
			
		||||
        return self.program.title + ' on ' + str(self.start) \
 | 
			
		||||
               + str(self.type)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        verbose_name = _('Diffusion')
 | 
			
		||||
        verbose_name_plural = _('Diffusions')
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user