diff --git a/programs/admin.py b/programs/admin.py index 955c8ec..9e239be 100755 --- a/programs/admin.py +++ b/programs/admin.py @@ -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) diff --git a/programs/autocomplete_light_registry.py b/programs/autocomplete_light_registry.py new file mode 100644 index 0000000..fd75aac --- /dev/null +++ b/programs/autocomplete_light_registry.py @@ -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''' + %s + ''' + + 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) + + diff --git a/programs/forms.py b/programs/forms.py new file mode 100644 index 0000000..828d541 --- /dev/null +++ b/programs/forms.py @@ -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'), + } + diff --git a/programs/models.py b/programs/models.py index d2f402d..4e9dedd 100755 --- a/programs/models.py +++ b/programs/models.py @@ -49,10 +49,10 @@ ugettext_lazy('one on two') DiffusionType = { - 'diffuse': 0x01 # the diffusion is planified or done - , 'cancel': 0x03 # the diffusion has been canceled from grid; useful to give + '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') diff --git a/templates/admin/base_site.html b/templates/admin/base_site.html index a040f36..1e8b0e8 100644 --- a/templates/admin/base_site.html +++ b/templates/admin/base_site.html @@ -1,8 +1,33 @@ {% extends "admin/base.html" %} {% block extrahead %} +{% include 'autocomplete_light/static.html' %} {% endblock %}