update tracks, work on admin, fix stuff and models
This commit is contained in:
		@ -1,11 +1,11 @@
 | 
			
		||||
import copy
 | 
			
		||||
 | 
			
		||||
from django.contrib     import admin
 | 
			
		||||
from django.forms       import Textarea
 | 
			
		||||
from django.db          import models
 | 
			
		||||
 | 
			
		||||
# import autocomplete_light as al
 | 
			
		||||
from suit.admin import SortableTabularInline
 | 
			
		||||
 | 
			
		||||
from programs.forms     import *
 | 
			
		||||
from programs.models    import *
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -13,13 +13,6 @@ from programs.models    import *
 | 
			
		||||
# Inlines
 | 
			
		||||
#
 | 
			
		||||
# TODO: inherits from the corresponding admin view
 | 
			
		||||
class SoundInline (admin.TabularInline):
 | 
			
		||||
    model = Sound
 | 
			
		||||
    raw_id_fields=('parent',)
 | 
			
		||||
    fields = ('title', 'private', 'tags', 'file', 'duration', 'fragment')
 | 
			
		||||
    extra = 1
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ScheduleInline (admin.TabularInline):
 | 
			
		||||
    model = Schedule
 | 
			
		||||
    extra = 1
 | 
			
		||||
@ -27,11 +20,20 @@ class ScheduleInline (admin.TabularInline):
 | 
			
		||||
 | 
			
		||||
class DiffusionInline (admin.TabularInline):
 | 
			
		||||
    model = Diffusion
 | 
			
		||||
    raw_id_fields=('parent',)
 | 
			
		||||
    fields = ('parent', 'type', 'date')
 | 
			
		||||
    fields = ('episode', 'type', 'begin', 'end', 'stream')
 | 
			
		||||
    readonly_fields = ('begin', 'end', 'stream')
 | 
			
		||||
    extra = 1
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TrackInline (SortableTabularInline):
 | 
			
		||||
    fields = ['artist', 'title', 'tags', 'position']
 | 
			
		||||
    form = TrackForm
 | 
			
		||||
    model = Track
 | 
			
		||||
    sortable = 'position'
 | 
			
		||||
    extra = 10
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# Parents
 | 
			
		||||
#
 | 
			
		||||
@ -41,7 +43,7 @@ class MetadataAdmin (admin.ModelAdmin):
 | 
			
		||||
            'fields': [ 'title', 'tags' ]
 | 
			
		||||
        }),
 | 
			
		||||
        ( None, {
 | 
			
		||||
            'fields': [ 'date' ],
 | 
			
		||||
            'fields': [ 'date', 'public', 'enumerable' ],
 | 
			
		||||
        }),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
@ -52,17 +54,19 @@ class MetadataAdmin (admin.ModelAdmin):
 | 
			
		||||
        obj.save()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
from autocomplete_light.contrib.taggit_field import TaggitWidget, TaggitField
 | 
			
		||||
class PublicationAdmin (MetadataAdmin):
 | 
			
		||||
    fieldsets = copy.deepcopy(MetadataAdmin.fieldsets)
 | 
			
		||||
 | 
			
		||||
    list_display = ('id', 'title', 'date', 'private')
 | 
			
		||||
    list_filter = ['date', 'private']
 | 
			
		||||
    list_display = ('id', 'title', 'date', 'public', 'parent')
 | 
			
		||||
    list_filter = ['date', 'public', 'parent', 'author']
 | 
			
		||||
    search_fields = ['title', 'content']
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    fieldsets[0][1]['fields'].insert(1, 'subtitle')
 | 
			
		||||
    fieldsets[0][1]['fields'] += [ 'img', 'content' ]
 | 
			
		||||
    fieldsets[1][1]['fields'] += [ 'parent', 'private', 'can_comment' ] #, 'meta' ],
 | 
			
		||||
    fieldsets[1][1]['fields'] += [ 'parent' ] #, 'meta' ],
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
@ -90,17 +94,19 @@ class ProgramAdmin (PublicationAdmin):
 | 
			
		||||
 | 
			
		||||
class EpisodeAdmin (PublicationAdmin):
 | 
			
		||||
    fieldsets = copy.deepcopy(PublicationAdmin.fieldsets)
 | 
			
		||||
    #inlines             = [ SoundInline ]
 | 
			
		||||
    list_filter         = ['parent'] + PublicationAdmin.list_filter
 | 
			
		||||
 | 
			
		||||
    # FIXME later: when we have thousands of tracks
 | 
			
		||||
    fieldsets[0][1]['fields'] += ['tracks']
 | 
			
		||||
    fieldsets[0][1]['fields'] += ['sounds']
 | 
			
		||||
 | 
			
		||||
    raw_id_fields = ('tracks', 'sounds')
 | 
			
		||||
    autocomplete_lookup_fields = {
 | 
			
		||||
        'm2m': ['tracks', 'sounds'],
 | 
			
		||||
    }
 | 
			
		||||
    inlines = (TrackInline, DiffusionInline)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DiffusionAdmin (admin.ModelAdmin):
 | 
			
		||||
    list_display = ('type', 'begin', 'end', 'episode', 'program', 'stream')
 | 
			
		||||
    list_filter = ('type', 'begin', 'program', 'stream')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
admin.site.register(Track)
 | 
			
		||||
admin.site.register(Sound, SoundAdmin)
 | 
			
		||||
@ -108,5 +114,5 @@ admin.site.register(Schedule)
 | 
			
		||||
admin.site.register(Article, ArticleAdmin)
 | 
			
		||||
admin.site.register(Program, ProgramAdmin)
 | 
			
		||||
admin.site.register(Episode, EpisodeAdmin)
 | 
			
		||||
admin.site.register(Diffusion)
 | 
			
		||||
admin.site.register(Diffusion, DiffusionAdmin)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										50
									
								
								programs/autocomplete_light_registry.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								programs/autocomplete_light_registry.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,50 @@
 | 
			
		||||
import autocomplete_light.shortcuts as al
 | 
			
		||||
from programs.models import *
 | 
			
		||||
 | 
			
		||||
from taggit.models import Tag
 | 
			
		||||
al.register(Tag)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class OneFieldAutocomplete(al.AutocompleteModelBase):
 | 
			
		||||
    choice_html_format = u'''
 | 
			
		||||
        <span class="block" data-value="%s">%s</span>
 | 
			
		||||
    '''
 | 
			
		||||
 | 
			
		||||
    def choice_html (self, choice):
 | 
			
		||||
        value = choice[self.search_fields[0]]
 | 
			
		||||
        return self.choice_html_format % (self.choice_label(choice),
 | 
			
		||||
            self.choice_label(value))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def choices_for_request(self):
 | 
			
		||||
        #if not self.request.user.is_staff:
 | 
			
		||||
        #    self.choices = self.choices.filter(private=False)
 | 
			
		||||
        filter_args = { self.search_fields[0] + '__icontains': self.request.GET['q'] }
 | 
			
		||||
 | 
			
		||||
        self.choices = self.choices.filter(**filter_args)
 | 
			
		||||
        self.choices = self.choices.values(self.search_fields[0]).distinct()
 | 
			
		||||
        return self.choices
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TrackArtistAutocomplete(OneFieldAutocomplete):
 | 
			
		||||
    search_fields = ['artist']
 | 
			
		||||
    model = Track
 | 
			
		||||
 | 
			
		||||
al.register(TrackArtistAutocomplete)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TrackTitleAutocomplete(OneFieldAutocomplete):
 | 
			
		||||
    search_fields = ['title']
 | 
			
		||||
    model = Track
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
al.register(TrackTitleAutocomplete)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#class DiffusionAutocomplete(OneFieldAutocomplete):
 | 
			
		||||
#    search_fields = ['episode', 'program', 'start', 'stop']
 | 
			
		||||
#    model = Diffusion
 | 
			
		||||
#
 | 
			
		||||
#al.register(DiffusionAutocomplete)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										19
									
								
								programs/forms.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								programs/forms.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,19 @@
 | 
			
		||||
from django import forms
 | 
			
		||||
from django.contrib.admin import widgets
 | 
			
		||||
 | 
			
		||||
import autocomplete_light.shortcuts as al
 | 
			
		||||
from autocomplete_light.contrib.taggit_field import TaggitWidget
 | 
			
		||||
 | 
			
		||||
from programs.models import *
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TrackForm (forms.ModelForm):
 | 
			
		||||
    class Meta:
 | 
			
		||||
        model = Track
 | 
			
		||||
        fields = ['artist', 'title', 'tags', 'position']
 | 
			
		||||
        widgets = {
 | 
			
		||||
            'artist': al.TextWidget('TrackArtistAutocomplete'),
 | 
			
		||||
            'title': al.TextWidget('TrackTitleAutocomplete'),
 | 
			
		||||
            'tags': TaggitWidget('TagAutocomplete'),
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -50,9 +50,9 @@ 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
 | 
			
		||||
  , 'stop':     0x04   # the diffusion been arbitrary stopped (non-stop or not)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -107,10 +107,15 @@ class Metadata (Model):
 | 
			
		||||
                      _('date')
 | 
			
		||||
                    , default = timezone.datetime.now
 | 
			
		||||
                  )
 | 
			
		||||
    private     = models.BooleanField(
 | 
			
		||||
                      _('private')
 | 
			
		||||
                    , default = False
 | 
			
		||||
                    , help_text = _('publication is private')
 | 
			
		||||
    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')
 | 
			
		||||
@ -143,7 +148,7 @@ class Publication (Metadata):
 | 
			
		||||
                      _('content')
 | 
			
		||||
                    , blank = True
 | 
			
		||||
                  )
 | 
			
		||||
    can_comment = models.BooleanField(
 | 
			
		||||
    commentable = models.BooleanField(
 | 
			
		||||
                      _('enable comments')
 | 
			
		||||
                    , default = True
 | 
			
		||||
                    , help_text = _('comments are enabled on this publication')
 | 
			
		||||
@ -209,32 +214,31 @@ class Publication (Metadata):
 | 
			
		||||
# 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
 | 
			
		||||
                    , blank = True
 | 
			
		||||
                  )
 | 
			
		||||
    title       = models.CharField(
 | 
			
		||||
                      _('title')
 | 
			
		||||
                    , max_length = 128
 | 
			
		||||
                  )
 | 
			
		||||
    version     = models.CharField(
 | 
			
		||||
                      _('version')
 | 
			
		||||
                    , max_length = 128
 | 
			
		||||
                    , blank = True
 | 
			
		||||
                    , help_text = _('additional informations on that track')
 | 
			
		||||
                  )
 | 
			
		||||
    tags        = TaggableManager( blank = True )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def autocomplete_search_fields():
 | 
			
		||||
        return ("artist__icontains", 'title__icontains')
 | 
			
		||||
    # position can be used to specify a position in seconds
 | 
			
		||||
    position    = models.SmallIntegerField(
 | 
			
		||||
                      default = 0
 | 
			
		||||
                    , help_text=_('position in the playlist')
 | 
			
		||||
                  )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        return ' '.join([self.artist, ':', self.title,
 | 
			
		||||
                (self.version and ('(' + self.version + ')') or '') ])
 | 
			
		||||
        return ' '.join([self.artist, ':', self.title])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
@ -532,12 +536,14 @@ class Program (Publication):
 | 
			
		||||
                  , null = True
 | 
			
		||||
                  , help_text = _('parent article')
 | 
			
		||||
                  )
 | 
			
		||||
 | 
			
		||||
    email       = models.EmailField(
 | 
			
		||||
                    _('email')
 | 
			
		||||
                  , max_length = 128
 | 
			
		||||
                  , null = True
 | 
			
		||||
                  , blank = True
 | 
			
		||||
                  )
 | 
			
		||||
 | 
			
		||||
    url         = models.URLField(
 | 
			
		||||
                    _('website')
 | 
			
		||||
                  , blank = True
 | 
			
		||||
@ -567,6 +573,7 @@ class Program (Publication):
 | 
			
		||||
        verbose_name_plural = _('Programs')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Episode (Publication):
 | 
			
		||||
    # Note:
 | 
			
		||||
    #   We do not especially need a duration here, because even if an
 | 
			
		||||
@ -581,11 +588,6 @@ class Episode (Publication):
 | 
			
		||||
                    , verbose_name = _('parent')
 | 
			
		||||
                    , help_text = _('parent program')
 | 
			
		||||
                  )
 | 
			
		||||
    tracks      = models.ManyToManyField(
 | 
			
		||||
                      Track
 | 
			
		||||
                    , blank = True
 | 
			
		||||
                    , verbose_name = _('tracks')
 | 
			
		||||
                  )
 | 
			
		||||
    sounds      = models.ManyToManyField(
 | 
			
		||||
                      Sound
 | 
			
		||||
                    , blank = True
 | 
			
		||||
@ -620,23 +622,24 @@ class Diffusion (Model):
 | 
			
		||||
                      verbose_name = _('type')
 | 
			
		||||
                    , choices = [ (y, x) for x,y in DiffusionType.items() ]
 | 
			
		||||
                  )
 | 
			
		||||
    date        = models.DateTimeField( _('date of diffusion start') )
 | 
			
		||||
    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'
 | 
			
		||||
                  )
 | 
			
		||||
    scheduled   = models.BooleanField(
 | 
			
		||||
                      verbose_name = _('scheduled')
 | 
			
		||||
                    , default = False
 | 
			
		||||
                    , help_text = 'diffusion generated automatically'
 | 
			
		||||
                  )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def save (self, *args, **kwargs):
 | 
			
		||||
        if self.episode:
 | 
			
		||||
            self.program = self.episode.parent
 | 
			
		||||
        super(Diffusion, self).save(*args, **kwargs)
 | 
			
		||||
 | 
			
		||||
    def __str__ (self):
 | 
			
		||||
        return self.program.title + ' on ' + str(self.start) \
 | 
			
		||||
               + str(self.type)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        verbose_name = _('Diffusion')
 | 
			
		||||
 | 
			
		||||
@ -1,8 +1,33 @@
 | 
			
		||||
{% extends "admin/base.html" %}
 | 
			
		||||
 | 
			
		||||
{% block extrahead %}
 | 
			
		||||
{% include 'autocomplete_light/static.html' %}
 | 
			
		||||
<style>
 | 
			
		||||
 | 
			
		||||
/** autocomplete override **/
 | 
			
		||||
.autocomplete-light-widget .deck [data-value] .remove {
 | 
			
		||||
  float: right;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.autocomplete-light-widget .deck [data-value],
 | 
			
		||||
.autocomplete-light-widget .deck .choice {
 | 
			
		||||
  display: block;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.control-group .add-related,
 | 
			
		||||
.inline-group .add-related {
 | 
			
		||||
  vertical-align: bottom;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/** suit **/
 | 
			
		||||
.controls textarea,
 | 
			
		||||
.controls .vTextField {
 | 
			
		||||
    width: calc(100% - 10px);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/** grappelli **/
 | 
			
		||||
.grp-autocomplete-wrapper-m2m:focus, .grp-autocomplete-wrapper-m2m.grp-state-focus,
 | 
			
		||||
.grp-autocomplete-wrapper-m2m {
 | 
			
		||||
  background: rgba(255, 255, 255, 0.2);
 | 
			
		||||
@ -19,6 +44,7 @@
 | 
			
		||||
  float: none;
 | 
			
		||||
  display: block;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
</style>
 | 
			
		||||
 | 
			
		||||
{% endblock %}
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user