admin
This commit is contained in:
		@ -1,7 +1,9 @@
 | 
			
		||||
import copy
 | 
			
		||||
 | 
			
		||||
from django.contrib     import admin
 | 
			
		||||
import programs.models  as models
 | 
			
		||||
from django.forms       import Textarea
 | 
			
		||||
from django.db          import models
 | 
			
		||||
from programs.models    import *
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
@ -9,26 +11,24 @@ import programs.models  as models
 | 
			
		||||
#
 | 
			
		||||
# TODO: inherits from the corresponding admin view
 | 
			
		||||
class SoundFileInline (admin.TabularInline):
 | 
			
		||||
    model = models.SoundFile
 | 
			
		||||
    model = SoundFile
 | 
			
		||||
    raw_id_fields=('parent',)
 | 
			
		||||
    fields = ('title', 'private', 'tags', 'file', 'duration', 'fragment')
 | 
			
		||||
    extra = 1
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class EpisodeInline (admin.StackedInline):
 | 
			
		||||
    model = models.Episode
 | 
			
		||||
    extra = 0
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ScheduleInline (admin.TabularInline):
 | 
			
		||||
    model = models.Schedule
 | 
			
		||||
    extra = 0
 | 
			
		||||
    model = Schedule
 | 
			
		||||
    extra = 1
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DiffusionInline (admin.TabularInline):
 | 
			
		||||
    model = Diffusion
 | 
			
		||||
    raw_id_fields=('parent',)
 | 
			
		||||
    fields = ('parent', 'type', 'date')
 | 
			
		||||
    extra = 1
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class EventInline (admin.StackedInline):
 | 
			
		||||
    model = models.Event
 | 
			
		||||
    extra = 0
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# Parents
 | 
			
		||||
#
 | 
			
		||||
@ -37,9 +37,8 @@ class MetadataAdmin (admin.ModelAdmin):
 | 
			
		||||
        ( None, {
 | 
			
		||||
            'fields': [ 'title', 'tags' ]
 | 
			
		||||
        }),
 | 
			
		||||
        ( 'metadata', {
 | 
			
		||||
        ( None, {
 | 
			
		||||
            'fields': [ 'date' ],
 | 
			
		||||
            'classes': ['collapse']
 | 
			
		||||
        }),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
@ -52,38 +51,31 @@ class MetadataAdmin (admin.ModelAdmin):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PublicationAdmin (MetadataAdmin):
 | 
			
		||||
    formfield_overrides = {
 | 
			
		||||
        models.TextField: {'widget': Textarea(attrs={'style':'width:calc(100% - 12px);'})},
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fieldsets = copy.deepcopy(MetadataAdmin.fieldsets)
 | 
			
		||||
 | 
			
		||||
    list_display = ('id', 'title', 'date', 'public')
 | 
			
		||||
    list_filter = ['date', 'public']
 | 
			
		||||
    list_display = ('id', 'title', 'date', 'private')
 | 
			
		||||
    list_filter = ['date', 'private']
 | 
			
		||||
    search_fields = ['title', 'content']
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def __init__ (self, *args, **kwargs):
 | 
			
		||||
        self.fieldsets[0][1]['fields'].insert(1, 'subtitle')
 | 
			
		||||
        self.fieldsets[0][1]['fields'] += [ 'img', 'content' ]
 | 
			
		||||
        self.fieldsets[1][1]['fields'] += [ 'parent', 'public', 'can_comment', 'meta' ],
 | 
			
		||||
        return super(PublicationAdmin, self).__init__(*args, **kwargs)
 | 
			
		||||
    fieldsets[0][1]['fields'].insert(1, 'subtitle')
 | 
			
		||||
    fieldsets[0][1]['fields'] += [ 'img', 'content' ]
 | 
			
		||||
    fieldsets[1][1]['fields'] += [ 'parent', 'private', 'can_comment' ] #, 'meta' ],
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# ModelAdmin list
 | 
			
		||||
#
 | 
			
		||||
#class TrackAdmin (MetadataAdmin):
 | 
			
		||||
#    fieldsets = [
 | 
			
		||||
#        (None, { 'fields': [ 'title', 'artist', 'version', 'tags'] } )
 | 
			
		||||
#    ]
 | 
			
		||||
 | 
			
		||||
class SoundFileAdmin (MetadataAdmin):
 | 
			
		||||
    fieldsets = [
 | 
			
		||||
        (None, { 'fields': ['title', 'tags', 'file', 'embed' ] } ),
 | 
			
		||||
        ('metadata', { 'fields': ['duration', 'date', 'podcastable', 'fragment' ] } )
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    #inlines = [ EpisodeInline ]
 | 
			
		||||
    #inlines = [ EventInline ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ArticleAdmin (PublicationAdmin):
 | 
			
		||||
    fieldsets           = copy.deepcopy(PublicationAdmin.fieldsets)
 | 
			
		||||
@ -91,13 +83,11 @@ class ArticleAdmin (PublicationAdmin):
 | 
			
		||||
    fieldsets[1][1]['fields'] += ['static_page']
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ProgramAdmin (PublicationAdmin):
 | 
			
		||||
    fieldsets           = copy.deepcopy(PublicationAdmin.fieldsets)
 | 
			
		||||
    inlines             = [ EpisodeInline, ScheduleInline ]
 | 
			
		||||
 | 
			
		||||
    fieldsets[1][1]['fields'] += ['email', 'url', 'non_stop']
 | 
			
		||||
    inlines             = [ ScheduleInline ]
 | 
			
		||||
 | 
			
		||||
    fieldsets[1][1]['fields'] += ['email', 'url']
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class EpisodeAdmin (PublicationAdmin):
 | 
			
		||||
@ -105,15 +95,16 @@ class EpisodeAdmin (PublicationAdmin):
 | 
			
		||||
    inlines             = [ SoundFileInline ]
 | 
			
		||||
    list_filter         = ['parent'] + PublicationAdmin.list_filter
 | 
			
		||||
 | 
			
		||||
    # FIXME later: when we have thousands of tracks
 | 
			
		||||
    fieldsets[0][1]['fields'] += ['tracks']
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
admin.site.register(models.Track)
 | 
			
		||||
admin.site.register(models.SoundFile, SoundFileAdmin)
 | 
			
		||||
admin.site.register(models.Schedule)
 | 
			
		||||
admin.site.register(models.Article, ArticleAdmin)
 | 
			
		||||
admin.site.register(models.Program, ProgramAdmin)
 | 
			
		||||
admin.site.register(models.Episode, EpisodeAdmin)
 | 
			
		||||
admin.site.register(models.Event)
 | 
			
		||||
admin.site.register(Track)
 | 
			
		||||
admin.site.register(SoundFile, SoundFileAdmin)
 | 
			
		||||
admin.site.register(Schedule)
 | 
			
		||||
admin.site.register(Article, ArticleAdmin)
 | 
			
		||||
admin.site.register(Program, ProgramAdmin)
 | 
			
		||||
admin.site.register(Episode, EpisodeAdmin)
 | 
			
		||||
admin.site.register(Diffusion)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -12,6 +12,7 @@ from django.utils.html                      import strip_tags
 | 
			
		||||
 | 
			
		||||
# extensions
 | 
			
		||||
from taggit.managers                        import TaggableManager
 | 
			
		||||
from sortedm2m.fields                       import SortedManyToManyField
 | 
			
		||||
 | 
			
		||||
import programs.settings                    as settings
 | 
			
		||||
 | 
			
		||||
@ -48,7 +49,7 @@ ugettext_lazy('second and fourth')
 | 
			
		||||
ugettext_lazy('one on two')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
EventType = {
 | 
			
		||||
DiffusionType = {
 | 
			
		||||
    '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
 | 
			
		||||
@ -107,10 +108,10 @@ class Metadata (Model):
 | 
			
		||||
                      _('date')
 | 
			
		||||
                    , default = timezone.datetime.now
 | 
			
		||||
                  )
 | 
			
		||||
    public      = models.BooleanField(
 | 
			
		||||
                      _('public')
 | 
			
		||||
    private     = models.BooleanField(
 | 
			
		||||
                      _('private')
 | 
			
		||||
                    , default = False
 | 
			
		||||
                    , help_text = _('publication is public')
 | 
			
		||||
                    , help_text = _('publication is private')
 | 
			
		||||
                  )
 | 
			
		||||
    # FIXME: add a field to specify if the element should be listed or not
 | 
			
		||||
    meta        = models.TextField(
 | 
			
		||||
@ -250,6 +251,14 @@ class Track (Model):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class SoundFile (Metadata):
 | 
			
		||||
    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)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    parent      = models.ForeignKey(
 | 
			
		||||
                      'Episode'
 | 
			
		||||
                    , verbose_name = _('episode')
 | 
			
		||||
@ -258,7 +267,7 @@ class SoundFile (Metadata):
 | 
			
		||||
                  )
 | 
			
		||||
    file        = models.FileField( #FIXME: filefield
 | 
			
		||||
                      _('file')
 | 
			
		||||
                    , upload_to = lambda i, f: SoundFile.__upload_path(i,f)
 | 
			
		||||
                    , upload_to = get_upload_path
 | 
			
		||||
                  )
 | 
			
		||||
    duration    = models.TimeField(
 | 
			
		||||
                      _('duration')
 | 
			
		||||
@ -297,14 +306,6 @@ class SoundFile (Metadata):
 | 
			
		||||
        super(SoundFile, self).save(*args, **kwargs)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def __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)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def __str__ (self):
 | 
			
		||||
        return str(self.id) + ': ' + self.file.name
 | 
			
		||||
 | 
			
		||||
@ -482,11 +483,6 @@ class Program (Publication):
 | 
			
		||||
                  , blank = True
 | 
			
		||||
                  , null = True
 | 
			
		||||
                  )
 | 
			
		||||
    non_stop    = models.SmallIntegerField(
 | 
			
		||||
                    _('non-stop priority')
 | 
			
		||||
                  , help_text = _('this program can be used as non-stop')
 | 
			
		||||
                  , default = -1
 | 
			
		||||
                  )
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def path (self):
 | 
			
		||||
@ -526,10 +522,9 @@ class Episode (Publication):
 | 
			
		||||
                      Program
 | 
			
		||||
                    , verbose_name = _('parent')
 | 
			
		||||
                  )
 | 
			
		||||
    tracks      = models.ManyToManyField(
 | 
			
		||||
    tracks      = SortedManyToManyField(
 | 
			
		||||
                      Track
 | 
			
		||||
                    , verbose_name = _('playlist')
 | 
			
		||||
                    , blank = True
 | 
			
		||||
                    , verbose_name = _('tracks')
 | 
			
		||||
                  )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -539,11 +534,11 @@ class Episode (Publication):
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Event (Model):
 | 
			
		||||
class Diffusion (Model):
 | 
			
		||||
    """
 | 
			
		||||
    Event logs and planifications.
 | 
			
		||||
    Diffusion logs and planifications.
 | 
			
		||||
 | 
			
		||||
    An event is:
 | 
			
		||||
    A diffusion is:
 | 
			
		||||
    - scheduled: when it has been generated following programs' Schedule
 | 
			
		||||
    - planified: when it has been generated manually/ponctually or scheduled
 | 
			
		||||
    """
 | 
			
		||||
@ -556,23 +551,29 @@ class Event (Model):
 | 
			
		||||
                      Program
 | 
			
		||||
                  )
 | 
			
		||||
    type        = models.SmallIntegerField(
 | 
			
		||||
                      _('type')
 | 
			
		||||
                    , choices = [ (y, x) for x,y in EventType.items() ]
 | 
			
		||||
                      verbose_name = _('type')
 | 
			
		||||
                    , choices = [ (y, x) for x,y in DiffusionType.items() ]
 | 
			
		||||
                  )
 | 
			
		||||
    date        = models.DateTimeField( _('date of event start') )
 | 
			
		||||
    date        = models.DateTimeField( _('date of diffusion start') )
 | 
			
		||||
    stream      = models.SmallIntegerField(
 | 
			
		||||
                      _('stream')
 | 
			
		||||
                      verbose_name = _('stream')
 | 
			
		||||
                    , default = 0
 | 
			
		||||
                    , help_text = 'stream id on which the event happens'
 | 
			
		||||
                    , help_text = 'stream id on which the diffusion happens'
 | 
			
		||||
                  )
 | 
			
		||||
    scheduled   = models.BooleanField(
 | 
			
		||||
                      _('automated')
 | 
			
		||||
                      verbose_name = _('automated')
 | 
			
		||||
                    , default = False
 | 
			
		||||
                    , help_text = 'event generated automatically'
 | 
			
		||||
                    , help_text = 'diffusion generated automatically'
 | 
			
		||||
                  )
 | 
			
		||||
 | 
			
		||||
    def save (self, *args, **kwargs):
 | 
			
		||||
        if self.parent:
 | 
			
		||||
            self.program = self.parent.parent
 | 
			
		||||
        super(Diffusion, self).save(*args, **kwargs)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        verbose_name = _('Event')
 | 
			
		||||
        verbose_name_plural = _('Events')
 | 
			
		||||
        verbose_name = _('Diffusion')
 | 
			
		||||
        verbose_name_plural = _('Diffusions')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,5 @@
 | 
			
		||||
Django>=1.9.0
 | 
			
		||||
taggit
 | 
			
		||||
taggit>=0.12.1
 | 
			
		||||
sortedm2m>=1.0.2
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -18,7 +18,7 @@ ensure('AIRCOX_SOUNDFILE_DEFAULT_DIR',
 | 
			
		||||
ensure('AIRCOX_SOUNDFILE_EXT',
 | 
			
		||||
        ('ogg','flac','wav','mp3','opus'))
 | 
			
		||||
 | 
			
		||||
# Stream for the scheduled events
 | 
			
		||||
# Stream for the scheduled diffusions
 | 
			
		||||
ensure('AIRCOX_SCHEDULED_STREAM', 0)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,31 +1,31 @@
 | 
			
		||||
from django.utils                   import timezone
 | 
			
		||||
 | 
			
		||||
from programs.models                import Schedule, Event, Episode,\
 | 
			
		||||
                                           EventType
 | 
			
		||||
from programs.models                import Schedule, Diffusion, Episode,\
 | 
			
		||||
                                           DiffusionType
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def scheduled_month_events (date = None, unsaved_only = False):
 | 
			
		||||
def scheduled_month_diffusions (date = None, unsaved_only = False):
 | 
			
		||||
    """
 | 
			
		||||
    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
 | 
			
		||||
    Return a list of scheduled diffusions for the month of the given date. For the
 | 
			
		||||
    non existing diffusions, a program attribute to the corresponding program is
 | 
			
		||||
    set.
 | 
			
		||||
    """
 | 
			
		||||
    if not date:
 | 
			
		||||
        date = timezone.datetime.today()
 | 
			
		||||
 | 
			
		||||
    schedules = Schedule.objects.all()
 | 
			
		||||
    events = []
 | 
			
		||||
    diffusions = []
 | 
			
		||||
 | 
			
		||||
    for schedule in schedules:
 | 
			
		||||
        dates = schedule.dates_of_month()
 | 
			
		||||
        for date in dates:
 | 
			
		||||
            event = Event.objects \
 | 
			
		||||
            diffusion = Diffusion.objects \
 | 
			
		||||
                         .filter(date = date, parent__parent = schedule.parent)
 | 
			
		||||
 | 
			
		||||
            if event.count():
 | 
			
		||||
            if diffusion.count():
 | 
			
		||||
                if not unsaved_only:
 | 
			
		||||
                    events.append(event)
 | 
			
		||||
                    diffusions.append(diffusion)
 | 
			
		||||
                continue
 | 
			
		||||
 | 
			
		||||
            # get episode
 | 
			
		||||
@ -37,16 +37,16 @@ def scheduled_month_events (date = None, unsaved_only = False):
 | 
			
		||||
                                              , parent = schedule.parent )
 | 
			
		||||
            episode  = episode[0] if episode.count() else None
 | 
			
		||||
 | 
			
		||||
            # make event
 | 
			
		||||
            event = Event( parent = episode
 | 
			
		||||
            # make diffusion
 | 
			
		||||
            diffusion = Diffusion( parent = episode
 | 
			
		||||
                         , program = schedule.parent
 | 
			
		||||
                         , type = EventType['diffuse']
 | 
			
		||||
                         , type = DiffusionType['diffuse']
 | 
			
		||||
                         , date = date
 | 
			
		||||
                         , stream = settings.AIRCOX_SCHEDULED_STREAM
 | 
			
		||||
                         , scheduled = True
 | 
			
		||||
                         )
 | 
			
		||||
            event.program = schedule.program
 | 
			
		||||
            events.append(event)
 | 
			
		||||
    return events
 | 
			
		||||
            diffusion.program = schedule.program
 | 
			
		||||
            diffusions.append(diffusion)
 | 
			
		||||
    return diffusions
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -7,7 +7,7 @@ import programs.settings
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class EventList:
 | 
			
		||||
class DiffusionList:
 | 
			
		||||
    type  = None
 | 
			
		||||
    next  = None
 | 
			
		||||
    prev  = None
 | 
			
		||||
@ -22,29 +22,29 @@ class EventList:
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def get_queryset (self):
 | 
			
		||||
        events = models.Event.objects;
 | 
			
		||||
        diffusions = models.Diffusion.objects;
 | 
			
		||||
 | 
			
		||||
        if self.next:   events = events.filter( date_end__ge = timezone.now() )
 | 
			
		||||
        elif self.prev: events = events.filter( date_end__le = timezone.now() )
 | 
			
		||||
        else:           events = events.all()
 | 
			
		||||
        if self.next:   diffusions = diffusions.filter( date_end__ge = timezone.now() )
 | 
			
		||||
        elif self.prev: diffusions = diffusions.filter( date_end__le = timezone.now() )
 | 
			
		||||
        else:           diffusions = diffusions.all()
 | 
			
		||||
 | 
			
		||||
        events = events.extra(order_by = ['date'])
 | 
			
		||||
        if self.at:     events = events[self.at:]
 | 
			
		||||
        if self.count:  events = events[:self.count]
 | 
			
		||||
        diffusions = diffusions.extra(order_by = ['date'])
 | 
			
		||||
        if self.at:     diffusions = diffusions[self.at:]
 | 
			
		||||
        if self.count:  diffusions = diffusions[:self.count]
 | 
			
		||||
 | 
			
		||||
        self.events = events
 | 
			
		||||
        self.diffusions = diffusions
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def raw_string():
 | 
			
		||||
        """
 | 
			
		||||
        Return a string with events rendered as raw
 | 
			
		||||
        Return a string with diffusions rendered as raw
 | 
			
		||||
        """
 | 
			
		||||
        res = []
 | 
			
		||||
        for event in events:
 | 
			
		||||
            r = [ dateformat.format(event.date, "Y/m/d H:i:s")
 | 
			
		||||
                , str(event.type)
 | 
			
		||||
                , event.parent.file.path
 | 
			
		||||
                , event.parent.file.url
 | 
			
		||||
        for diffusion in diffusions:
 | 
			
		||||
            r = [ dateformat.format(diffusion.date, "Y/m/d H:i:s")
 | 
			
		||||
                , str(diffusion.type)
 | 
			
		||||
                , diffusion.parent.file.path
 | 
			
		||||
                , diffusion.parent.file.url
 | 
			
		||||
                ]
 | 
			
		||||
 | 
			
		||||
            res.push(' '.join(r))
 | 
			
		||||
@ -56,13 +56,13 @@ class EventList:
 | 
			
		||||
        import json
 | 
			
		||||
 | 
			
		||||
        res = []
 | 
			
		||||
        for event in events:
 | 
			
		||||
        for diffusion in diffusions:
 | 
			
		||||
            r = {
 | 
			
		||||
                  'date': dateformat.format(event.date, "Y/m/d H:i:s")
 | 
			
		||||
                , 'date_end': dateformat.format(event.date_end, "Y/m/d H:i:s")
 | 
			
		||||
                , 'type': str(event.type)
 | 
			
		||||
                , 'file_path': event.parent.file.path
 | 
			
		||||
                , 'file_url': event.parent.file.url
 | 
			
		||||
                  'date': dateformat.format(diffusion.date, "Y/m/d H:i:s")
 | 
			
		||||
                , 'date_end': dateformat.format(diffusion.date_end, "Y/m/d H:i:s")
 | 
			
		||||
                , 'type': str(diffusion.type)
 | 
			
		||||
                , 'file_path': diffusion.parent.file.path
 | 
			
		||||
                , 'file_url': diffusion.parent.file.url
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
            res.push(json.dumps(r))
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user