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