From abf444b8bcf51bbcf98e259a6d0c28bc72440ec3 Mon Sep 17 00:00:00 2001 From: Guillaume Pellerin Date: Thu, 13 Oct 2016 16:58:11 +0200 Subject: [PATCH] Unify Audio and Video into Media, then generic Playlist everywhere --- app/organization/agenda/admin.py | 13 +- .../migrations/0009_auto_20161013_1631.py | 40 +++++ app/organization/agenda/models.py | 18 +- app/organization/agenda/translation.py | 10 +- app/organization/magazine/admin.py | 12 +- .../migrations/0011_auto_20161013_1631.py | 43 +++++ .../migrations/0012_auto_20161013_1631.py | 28 +++ app/organization/magazine/models.py | 11 +- app/organization/magazine/translation.py | 10 +- app/organization/media/admin.py | 19 +- .../migrations/0006_auto_20161013_1631.py | 164 ++++++++++++++++++ .../migrations/0007_auto_20161013_1631.py | 53 ++++++ app/organization/media/models.py | 112 +++++++----- app/organization/media/translation.py | 16 +- app/organization/media/urls.py | 9 +- app/organization/media/views.py | 93 ++-------- app/organization/network/admin.py | 31 ++-- .../migrations/0038_auto_20161013_1631.py | 83 +++++++++ app/organization/network/models.py | 18 +- app/organization/network/translation.py | 20 +-- app/organization/pages/admin.py | 13 +- app/organization/pages/forms.py | 6 +- .../migrations/0008_auto_20161013_1631.py | 40 +++++ app/organization/pages/models.py | 18 +- app/organization/pages/translation.py | 10 +- app/organization/pages/views.py | 10 +- app/organization/projects/admin.py | 14 +- .../migrations/0020_auto_20161013_1631.py | 50 ++++++ app/organization/projects/models.py | 9 +- app/organization/projects/translation.py | 10 +- app/organization/shop/admin.py | 1 - 31 files changed, 666 insertions(+), 318 deletions(-) create mode 100644 app/organization/agenda/migrations/0009_auto_20161013_1631.py create mode 100644 app/organization/magazine/migrations/0011_auto_20161013_1631.py create mode 100644 app/organization/magazine/migrations/0012_auto_20161013_1631.py create mode 100644 app/organization/media/migrations/0006_auto_20161013_1631.py create mode 100644 app/organization/media/migrations/0007_auto_20161013_1631.py create mode 100644 app/organization/network/migrations/0038_auto_20161013_1631.py create mode 100644 app/organization/pages/migrations/0008_auto_20161013_1631.py create mode 100644 app/organization/projects/migrations/0020_auto_20161013_1631.py diff --git a/app/organization/agenda/admin.py b/app/organization/agenda/admin.py index f5a4d565..5a1bed38 100644 --- a/app/organization/agenda/admin.py +++ b/app/organization/agenda/admin.py @@ -27,14 +27,9 @@ class EventImageInline(TabularDynamicInlineAdmin): model = EventImage -class EventAudioInline(StackedDynamicInlineAdmin): +class EventPlaylistInline(StackedDynamicInlineAdmin): - model = EventAudio - - -class EventVideoInline(StackedDynamicInlineAdmin): - - model = EventVideo + model = EventPlaylist class EventDepartmentInline(TabularDynamicInlineAdmin): @@ -73,8 +68,8 @@ class CustomEventAdmin(EventAdmin): if settings.EVENT_USE_FEATURED_IMAGE: list_display.insert(0, "admin_thumb") list_filter = deepcopy(DisplayableAdmin.list_filter) + ("location", "category") - inlines = [EventPeriodInline, EventBlockInline, EventImageInline, EventDepartmentInline, EventPersonInline, - EventLinkInline, EventAudioInline, EventVideoInline, EventTrainingInline] + inlines = [EventPeriodInline, EventBlockInline, EventImageInline, EventDepartmentInline, + EventPersonInline, EventLinkInline, EventPlaylistInline, EventTrainingInline] def save_form(self, request, form, change): """ diff --git a/app/organization/agenda/migrations/0009_auto_20161013_1631.py b/app/organization/agenda/migrations/0009_auto_20161013_1631.py new file mode 100644 index 00000000..d3aa4b02 --- /dev/null +++ b/app/organization/agenda/migrations/0009_auto_20161013_1631.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.10 on 2016-10-13 14:31 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('organization-media', '0005_auto_20160930_1849'), + ('mezzanine_agenda', '0013_auto_20161010_1130'), + ('organization-agenda', '0008_auto_20161005_1455'), + ] + + operations = [ + migrations.CreateModel( + name='EventPlaylist', + fields=[ + ('playlist_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='organization-media.Playlist')), + ('event', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='playlists', to='mezzanine_agenda.Event', verbose_name='event')), + ], + options={ + 'verbose_name': 'playlist', + 'verbose_name_plural': 'playlists', + }, + bases=('organization-media.playlist',), + ), + migrations.DeleteModel( + name='EventAudio', + ), + migrations.DeleteModel( + name='EventVideo', + ), + migrations.AlterOrderWithRespectTo( + name='eventplaylist', + order_with_respect_to='event', + ), + ] diff --git a/app/organization/agenda/models.py b/app/organization/agenda/models.py index 4972904e..288f90a3 100644 --- a/app/organization/agenda/models.py +++ b/app/organization/agenda/models.py @@ -57,23 +57,13 @@ class EventLink(Link): verbose_name_plural = _("links") -class EventAudio(Audio): +class EventPlaylist(Playlist): - event = models.ForeignKey(Event, verbose_name=_('event'), related_name='audios', blank=True, null=True, on_delete=models.SET_NULL) + event = models.ForeignKey(Event, verbose_name=_('event'), related_name='playlists', blank=True, null=True, on_delete=models.SET_NULL) class Meta: - verbose_name = _("audio") - verbose_name_plural = _("audios") - order_with_respect_to = "event" - - -class EventVideo(Video): - - event = models.ForeignKey(Event, verbose_name=_('event'), related_name='videos', blank=True, null=True, on_delete=models.SET_NULL) - - class Meta: - verbose_name = _("video") - verbose_name_plural = _("videos") + verbose_name = _("playlist") + verbose_name_plural = _("playlists") order_with_respect_to = "event" diff --git a/app/organization/agenda/translation.py b/app/organization/agenda/translation.py index 7d45af7f..86254672 100644 --- a/app/organization/agenda/translation.py +++ b/app/organization/agenda/translation.py @@ -15,14 +15,8 @@ class EventImageTranslationOptions(TranslationOptions): fields = () -@register(EventAudio) -class EventAudioTranslationOptions(TranslationOptions): - - fields = () - - -@register(EventVideo) -class EventVideoTranslationOptions(TranslationOptions): +@register(EventPlaylist) +class EventPlaylistTranslationOptions(TranslationOptions): fields = () diff --git a/app/organization/magazine/admin.py b/app/organization/magazine/admin.py index d397a160..cf7e863c 100644 --- a/app/organization/magazine/admin.py +++ b/app/organization/magazine/admin.py @@ -12,14 +12,9 @@ class ArticleImageInline(TabularDynamicInlineAdmin): model = ArticleImage -class ArticleVideoInline(StackedDynamicInlineAdmin): +class ArticlePlaylistInline(TabularDynamicInlineAdmin): - model = ArticleVideo - - -class ArticleAudioInline(StackedDynamicInlineAdmin): - - model = ArticleAudio + model = ArticlePlaylist class ArticleAdmin(admin.ModelAdmin): @@ -52,8 +47,7 @@ class ArticleAdminDisplayable(DisplayableAdmin): inlines = [ArticleImageInline, ArticlePersonAutocompleteInlineAdmin, DynamicContentArticleInline, - ArticleVideoInline, - ArticleAudioInline] + ArticlePlaylistInline] class BriefAdmin(admin.ModelAdmin): #OrderableTabularInline diff --git a/app/organization/magazine/migrations/0011_auto_20161013_1631.py b/app/organization/magazine/migrations/0011_auto_20161013_1631.py new file mode 100644 index 00000000..cbea17c3 --- /dev/null +++ b/app/organization/magazine/migrations/0011_auto_20161013_1631.py @@ -0,0 +1,43 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.10 on 2016-10-13 14:31 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('organization-media', '0005_auto_20160930_1849'), + ('organization-magazine', '0010_articleaudio_articlevideo'), + ] + + operations = [ + migrations.CreateModel( + name='ArticlePlaylist', + fields=[ + ('playlist_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='organization-media.Playlist')), + ], + options={ + 'abstract': False, + }, + bases=('organization-media.playlist',), + ), + migrations.RemoveField( + model_name='articleaudio', + name='article', + ), + migrations.RemoveField( + model_name='articleaudio', + name='audio_ptr', + ), + migrations.RemoveField( + model_name='articlevideo', + name='article', + ), + migrations.RemoveField( + model_name='articlevideo', + name='video_ptr', + ), + ] diff --git a/app/organization/magazine/migrations/0012_auto_20161013_1631.py b/app/organization/magazine/migrations/0012_auto_20161013_1631.py new file mode 100644 index 00000000..0bc550bb --- /dev/null +++ b/app/organization/magazine/migrations/0012_auto_20161013_1631.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.10 on 2016-10-13 14:31 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('organization-magazine', '0011_auto_20161013_1631'), + ('organization-media', '0006_auto_20161013_1631'), + ] + + operations = [ + migrations.DeleteModel( + name='ArticleAudio', + ), + migrations.DeleteModel( + name='ArticleVideo', + ), + migrations.AddField( + model_name='articleplaylist', + name='article', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='playlists', to='organization-magazine.Article', verbose_name='article'), + ), + ] diff --git a/app/organization/magazine/models.py b/app/organization/magazine/models.py index 90dba922..b044f0a5 100644 --- a/app/organization/magazine/models.py +++ b/app/organization/magazine/models.py @@ -12,7 +12,7 @@ from mezzanine.core.models import RichText, Displayable, Slugged from mezzanine.pages.models import Page from mezzanine.blog.models import BlogPost from organization.network.models import Department, PersonListBlock -from organization.media.models import Audio, Video +from organization.media.models import Playlist from organization.core.models import * from organization.magazine.apps import * @@ -40,14 +40,9 @@ class ArticleImage(Image): order_with_respect_to = "article" -class ArticleAudio(Audio): +class ArticlePlaylist(Playlist): - article = models.ForeignKey(Article, verbose_name=_('article'), related_name='audios', blank=True, null=True, on_delete=models.SET_NULL) - - -class ArticleVideo(Video): - - article = models.ForeignKey(Article, verbose_name=_('article'), related_name='videos', blank=True, null=True, on_delete=models.SET_NULL) + article = models.ForeignKey(Article, verbose_name=_('article'), related_name='playlists', blank=True, null=True, on_delete=models.SET_NULL) class Brief(Displayable, RichText): #Orderable diff --git a/app/organization/magazine/translation.py b/app/organization/magazine/translation.py index ac7b7a09..e52ad434 100644 --- a/app/organization/magazine/translation.py +++ b/app/organization/magazine/translation.py @@ -43,13 +43,7 @@ class DynamicContentArticleTranslationOptions(TranslationOptions): pass -@register(ArticleAudio) -class ArticleAudioTranslationOptions(TranslationOptions): - - pass - - -@register(ArticleVideo) -class ArticleVideoTranslationOptions(TranslationOptions): +@register(ArticlePlaylist) +class ArticlePlaylistTranslationOptions(TranslationOptions): pass diff --git a/app/organization/media/admin.py b/app/organization/media/admin.py index 54d87ff1..7e20aa10 100644 --- a/app/organization/media/admin.py +++ b/app/organization/media/admin.py @@ -4,21 +4,25 @@ from mezzanine.core.admin import * from organization.media.models import * -class VideoAdmin(BaseTranslationModelAdmin): +class MediaTranscodedAdmin(TabularDynamicInlineAdmin): - model = Video + model = MediaTranscoded -class AudioAdmin(BaseTranslationModelAdmin): +class MediaAdmin(BaseTranslationModelAdmin): - model = Audio + model = Media + inlines = (MediaTranscodedAdmin,) + +class PlaylistMediaInline(TabularDynamicInlineAdmin): + + model = PlaylistMedia class PlaylistAdmin(BaseTranslationModelAdmin): model = Playlist - list_display = ('__str__',) - filter_horizontal = ['audios'] + inlines = (PlaylistMediaInline,) class MediaCategoryAdmin(BaseTranslationModelAdmin): @@ -26,7 +30,6 @@ class MediaCategoryAdmin(BaseTranslationModelAdmin): model = MediaCategory -admin.site.register(Video, VideoAdmin) -admin.site.register(Audio, AudioAdmin) +admin.site.register(Media, MediaAdmin) admin.site.register(Playlist, PlaylistAdmin) admin.site.register(MediaCategory, MediaCategoryAdmin) diff --git a/app/organization/media/migrations/0006_auto_20161013_1631.py b/app/organization/media/migrations/0006_auto_20161013_1631.py new file mode 100644 index 00000000..09cd5b51 --- /dev/null +++ b/app/organization/media/migrations/0006_auto_20161013_1631.py @@ -0,0 +1,164 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.10 on 2016-10-13 14:31 +from __future__ import unicode_literals + +from django.db import migrations, models +import mezzanine.core.fields + + +class Migration(migrations.Migration): + + dependencies = [ + ('organization-media', '0005_auto_20160930_1849'), + ] + + operations = [ + migrations.CreateModel( + name='Media', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('keywords_string', models.CharField(blank=True, editable=False, max_length=500)), + ('title', models.CharField(max_length=500, verbose_name='Title')), + ('title_fr', models.CharField(max_length=500, null=True, verbose_name='Title')), + ('title_en', models.CharField(max_length=500, null=True, verbose_name='Title')), + ('slug', models.CharField(blank=True, help_text='Leave blank to have the URL auto-generated from the title.', max_length=2000, null=True, verbose_name='URL')), + ('_meta_title', models.CharField(blank=True, help_text='Optional title to be used in the HTML title tag. If left blank, the main title field will be used.', max_length=500, null=True, verbose_name='Title')), + ('description', models.TextField(blank=True, verbose_name='Description')), + ('description_fr', models.TextField(blank=True, null=True, verbose_name='Description')), + ('description_en', models.TextField(blank=True, null=True, verbose_name='Description')), + ('gen_description', models.BooleanField(default=True, help_text='If checked, the description will be automatically generated from content. Uncheck if you want to manually set a custom description.', verbose_name='Generate description')), + ('created', models.DateTimeField(editable=False, null=True)), + ('updated', models.DateTimeField(editable=False, null=True)), + ('status', models.IntegerField(choices=[(1, 'Draft'), (2, 'Published')], default=2, help_text='With Draft chosen, will only be shown for admin users on the site.', verbose_name='Status')), + ('publish_date', models.DateTimeField(blank=True, db_index=True, help_text="With Published chosen, won't be shown until this time", null=True, verbose_name='Published from')), + ('expiry_date', models.DateTimeField(blank=True, help_text="With Published chosen, won't be shown after this time", null=True, verbose_name='Expires on')), + ('short_url', models.URLField(blank=True, null=True)), + ('in_sitemap', models.BooleanField(default=True, verbose_name='Show in sitemap')), + ('external_id', models.CharField(max_length=128, verbose_name='media id')), + ('poster_url', models.URLField(blank=True, max_length=1024, verbose_name='poster')), + ('created_at', models.DateTimeField(auto_now=True)), + ], + options={ + 'verbose_name': 'media', + 'ordering': ('-created_at',), + 'verbose_name_plural': 'medias', + }, + ), + migrations.CreateModel( + name='MediaTranscoded', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('file', mezzanine.core.fields.FileField(max_length=1024, verbose_name='Image')), + ('url', models.URLField(blank=True, max_length=1024, verbose_name='URL')), + ('mime_type', models.CharField(max_length=64, verbose_name='mime type')), + ], + options={ + 'verbose_name': 'media', + 'verbose_name_plural': 'medias', + }, + ), + migrations.CreateModel( + name='PlaylistMedia', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ], + options={ + 'verbose_name': 'media', + 'verbose_name_plural': 'medias', + }, + ), + migrations.RemoveField( + model_name='audio', + name='category', + ), + migrations.RemoveField( + model_name='audio', + name='site', + ), + migrations.RemoveField( + model_name='video', + name='category', + ), + migrations.RemoveField( + model_name='video', + name='site', + ), + migrations.AlterModelOptions( + name='playlist', + options={'verbose_name': 'playlist', 'verbose_name_plural': 'playlists'}, + ), + migrations.RemoveField( + model_name='playlist', + name='audios', + ), + migrations.AddField( + model_name='playlist', + name='_meta_title', + field=models.CharField(blank=True, help_text='Optional title to be used in the HTML title tag. If left blank, the main title field will be used.', max_length=500, null=True, verbose_name='Title'), + ), + migrations.AddField( + model_name='playlist', + name='created', + field=models.DateTimeField(editable=False, null=True), + ), + migrations.AddField( + model_name='playlist', + name='expiry_date', + field=models.DateTimeField(blank=True, help_text="With Published chosen, won't be shown after this time", null=True, verbose_name='Expires on'), + ), + migrations.AddField( + model_name='playlist', + name='gen_description', + field=models.BooleanField(default=True, help_text='If checked, the description will be automatically generated from content. Uncheck if you want to manually set a custom description.', verbose_name='Generate description'), + ), + migrations.AddField( + model_name='playlist', + name='in_sitemap', + field=models.BooleanField(default=True, verbose_name='Show in sitemap'), + ), + migrations.AddField( + model_name='playlist', + name='keywords_string', + field=models.CharField(blank=True, editable=False, max_length=500), + ), + migrations.AddField( + model_name='playlist', + name='publish_date', + field=models.DateTimeField(blank=True, db_index=True, help_text="With Published chosen, won't be shown until this time", null=True, verbose_name='Published from'), + ), + migrations.AddField( + model_name='playlist', + name='short_url', + field=models.URLField(blank=True, null=True), + ), + migrations.AddField( + model_name='playlist', + name='status', + field=models.IntegerField(choices=[(1, 'Draft'), (2, 'Published')], default=2, help_text='With Draft chosen, will only be shown for admin users on the site.', verbose_name='Status'), + ), + migrations.AddField( + model_name='playlist', + name='type', + field=models.CharField(blank=True, choices=[('audio', 'audio'), ('video', 'video')], max_length=32, null=True, verbose_name='type'), + ), + migrations.AddField( + model_name='playlist', + name='updated', + field=models.DateTimeField(editable=False, null=True), + ), + migrations.AlterField( + model_name='playlist', + name='description', + field=models.TextField(blank=True, verbose_name='Description'), + ), + migrations.AlterField( + model_name='playlist', + name='description_en', + field=models.TextField(blank=True, null=True, verbose_name='Description'), + ), + migrations.AlterField( + model_name='playlist', + name='description_fr', + field=models.TextField(blank=True, null=True, verbose_name='Description'), + ), + ] diff --git a/app/organization/media/migrations/0007_auto_20161013_1631.py b/app/organization/media/migrations/0007_auto_20161013_1631.py new file mode 100644 index 00000000..dc78de06 --- /dev/null +++ b/app/organization/media/migrations/0007_auto_20161013_1631.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.10 on 2016-10-13 14:31 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('sites', '0002_alter_domain_unique'), + ('organization-pages', '0008_auto_20161013_1631'), + ('organization-magazine', '0012_auto_20161013_1631'), + ('organization-media', '0006_auto_20161013_1631'), + ('organization-network', '0038_auto_20161013_1631'), + ('organization-projects', '0020_auto_20161013_1631'), + ('organization-agenda', '0009_auto_20161013_1631'), + ] + + operations = [ + migrations.DeleteModel( + name='Audio', + ), + migrations.DeleteModel( + name='Video', + ), + migrations.AddField( + model_name='playlistmedia', + name='media', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='playlists', to='organization-media.Media', verbose_name='media'), + ), + migrations.AddField( + model_name='playlistmedia', + name='playlist', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='medias', to='organization-media.Playlist', verbose_name='playlist'), + ), + migrations.AddField( + model_name='mediatranscoded', + name='media', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='transcoded', to='organization-media.Media', verbose_name='media'), + ), + migrations.AddField( + model_name='media', + name='category', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='medias', to='organization-media.MediaCategory', verbose_name='category'), + ), + migrations.AddField( + model_name='media', + name='site', + field=models.ForeignKey(editable=False, on_delete=django.db.models.deletion.CASCADE, to='sites.Site'), + ), + ] diff --git a/app/organization/media/models.py b/app/organization/media/models.py index 35fcda66..8fd66b3a 100644 --- a/app/organization/media/models.py +++ b/app/organization/media/models.py @@ -13,78 +13,84 @@ from mezzanine_agenda.models import Event from django.conf import settings import requests + MEDIA_BASE_URL = getattr(settings, 'MEDIA_BASE_URL', 'http://medias.ircam.fr/embed/media/') +PLAYLIST_TYPE_CHOICES = [ + ('audio', _('audio')), + ('video', _('video')), +] class Media(Displayable): """Media""" - media_id = models.CharField(_('media id'), max_length=128) - open_source_url = models.URLField(_('open source URL'), max_length=1024, blank=True) - closed_source_url = models.URLField(_('closed source URL'), max_length=1024, blank=True) + external_id = models.CharField(_('media id'), max_length=128) poster_url = models.URLField(_('poster'), max_length=1024, blank=True) created_at = models.DateTimeField(auto_now=True) + category = models.ForeignKey('MediaCategory', verbose_name=_('category'), related_name='medias', blank=True, null=True, on_delete=models.SET_NULL) # objects = SearchableManager() search_fields = ("title",) class Meta: - abstract = True + verbose_name = "media" + verbose_name_plural = "medias" + ordering = ('-created_at',) - def __unicode__(self): + def __str__(self): return self.title + def get_absolute_url(self): + return reverse("organization-media-detail", kwargs={"slug": self.slug}) + @property def uri(self): - return MEDIA_BASE_URL + self.media_id + return MEDIA_BASE_URL + self.external_id def get_html(self): r = requests.get(self.uri) return r.content - def clean(self): - super(Media, self).clean() - self.q = pq(self.get_html()) - sources = self.q('source') - for source in sources: - if self.open_source_mime_type in source.attrib['type']: - self.open_source_url = source.attrib['src'] - elif self.closed_source_mime_type in source.attrib['type']: - self.closed_source_url = source.attrib['src'] - video = self.q('video') - if len(video): - if 'poster' in video[0].attrib.keys(): - self.poster_url = video[0].attrib['poster'] +def create_media(instance, created, raw, **kwargs): + # Ignore fixtures and saves for existing courses. + if not created or raw: + return -class Audio(Media): - """Audio""" + q = pq(instance.get_html()) + sources = q('source') - open_source_mime_type = 'audio/ogg' - closed_source_mime_type = 'audio/mp4' - category = models.ForeignKey('MediaCategory', verbose_name=_('category'), related_name='audios', blank=True, null=True, on_delete=models.SET_NULL) + video = q('video') + if len(video): + if 'poster' in video[0].attrib.keys(): + instance.poster_url = video[0].attrib['poster'] - class Meta: - verbose_name = _('audio') - ordering = ('-created_at',) + for source in sources: + mime_type = source.attrib['type'] + transcoded = MediaTranscoded(media=instance, mime_type=mime_type) + transcoded.url = source.attrib['src'] + transcoded.save() - def get_absolute_url(self): - return reverse("festival-audio-detail", kwargs={"slug": self.slug}) + instance.save() + +models.signals.post_save.connect(create_media, sender=Media, dispatch_uid='create_media') -class Video(Media): - """Video""" +class MediaTranscoded(models.Model): - open_source_mime_type = 'video/webm' - closed_source_mime_type = 'video/mp4' - category = models.ForeignKey('MediaCategory', verbose_name=_('category'), related_name='videos', blank=True, null=True, on_delete=models.SET_NULL) + media = models.ForeignKey('Media', verbose_name=_('media'), related_name='transcoded') + file = FileField(_("Image"), max_length=1024, upload_to="uploads/media/") + url = models.URLField(_('URL'), max_length=1024, blank=True) + mime_type = models.CharField(_('mime type'), max_length=64) + + preferred_mime_type = ['video/webm', 'audio/ogg'] class Meta: - verbose_name = _('video') - ordering = ('-created_at',) + verbose_name = "media" + verbose_name_plural = "medias" - def get_absolute_url(self): - return reverse("festival-video-detail", kwargs={"slug": self.slug}) + def __str__(self): + return self.url class MediaCategory(Slugged, Description): @@ -95,16 +101,28 @@ class MediaCategory(Slugged, Description): verbose_name_plural = _('media categories') def count(self): - try: - return self.videos.published().count()+1 - except: - return self.audios.published().count()+1 + return self.medias.published().count()+1 -class Playlist(Slugged, Description): - """(Playlist description)""" +class Playlist(Displayable): + """Playlist""" - audios = models.ManyToManyField('Audio', verbose_name=_('audios'), related_name='playlists', blank=True) + type = models.CharField(_('type'), max_length=32, choices=PLAYLIST_TYPE_CHOICES, blank=True, null=True) - def __str__(self): - return self.title + class Meta: + verbose_name = _('playlist') + verbose_name_plural = _('playlists') + + def get_absolute_url(self): + return reverse("organization-playlist-detail", kwargs={"slug": self.slug}) + + +class PlaylistMedia(models.Model): + """Playlist media""" + + playlist = models.ForeignKey(Playlist, verbose_name=_('playlist'), related_name='medias', blank=True, null=True, on_delete=models.SET_NULL) + media = models.ForeignKey(Media, verbose_name=_('media'), related_name='playlists', blank=True, null=True, on_delete=models.SET_NULL) + + class Meta: + verbose_name = _('media') + verbose_name_plural = _('medias') diff --git a/app/organization/media/translation.py b/app/organization/media/translation.py index 433dd153..0192d929 100644 --- a/app/organization/media/translation.py +++ b/app/organization/media/translation.py @@ -3,16 +3,16 @@ from modeltranslation.translator import translator, register, TranslationOptions from organization.media.models import * -@register(Video) -class VideoTranslationOptions(TranslationOptions): +@register(Media) +class MediaTranslationOptions(TranslationOptions): fields = ('title', 'description',) -@register(Audio) -class AudioTranslationOptions(TranslationOptions): +@register(MediaTranscoded) +class MediaTranscodedTranslationOptions(TranslationOptions): - fields = ('title', 'description',) + fields = () @register(Playlist) @@ -21,6 +21,12 @@ class PlaylistTranslationOptions(TranslationOptions): fields = ('title', 'description',) +@register(PlaylistMedia) +class PlaylistMediaTranslationOptions(TranslationOptions): + + fields = () + + @register(MediaCategory) class MediaCategoryTranslationOptions(TranslationOptions): diff --git a/app/organization/media/urls.py b/app/organization/media/urls.py index 2e5763d5..55f1ad05 100644 --- a/app/organization/media/urls.py +++ b/app/organization/media/urls.py @@ -11,11 +11,6 @@ from organization.media.views import * urlpatterns = [ - url(r'^media-list/$', MediaListView.as_view(), name="media-list"), - url(r'^videos/$', VideoListView.as_view(), name="festival-video-list"), - url(r'^videos/detail/(?P.*)/$', VideoDetailView.as_view(), name="festival-video-detail"), - url(r'^videos/category/(?P.*)/$', VideoListCategoryView.as_view(), name="festival-video-list-category"), - url(r'^audios/detail/(?P.*)/$', AudioDetailView.as_view(), name="festival-audio-detail"), - url(r'^audios/category/(?P.*)/$', AudioListCategoryView.as_view(), name="festival-audio-list-category"), - + url(r'^playlist/list/$', PlaylistListView.as_view(), name="organization-playlist-list"), + url(r'^playlist/detail/(?P.*)/$', PlaylistDetailView.as_view(), name="organization-playlist-detail"), ] diff --git a/app/organization/media/views.py b/app/organization/media/views.py index 4ea5af32..37616eee 100644 --- a/app/organization/media/views.py +++ b/app/organization/media/views.py @@ -4,93 +4,32 @@ from organization.media.models import * from organization.core.views import * from dal import autocomplete from dal_select2_queryset_sequence.views import Select2QuerySetSequenceView -from mezzanine_agenda.models import Event -from organization.agenda.models import EventVideo -from organization.magazine.models import Article, Topic, Brief, ArticleVideo -class VideoListView(ListView): +class PlaylistDetailView(SlugMixin, DetailView): - model = Video - template_name='festival/video_list.html' - - def get_queryset(self, **kwargs): - return self.model.objects.published() - - def get_context_data(self, **kwargs): - context = super(VideoListView, self).get_context_data(**kwargs) - context['categories'] = MediaCategory.objects.all() - return context - - -class VideoDetailView(SlugMixin, DetailView): - - model = Video - template_name='festival/video_detail.html' - context_object_name = 'video' - - def get_context_data(self, **kwargs): - context = super(VideoDetailView, self).get_context_data(**kwargs) - return context - - -class VideoListCategoryView(VideoListView): - - def get_queryset(self): - self.category = MediaCategory.objects.get(slug=self.kwargs['slug']) - return self.model.objects.filter(category=self.category) - - def get_context_data(self, **kwargs): - context = super(VideoListCategoryView, self).get_context_data(**kwargs) - context['category'] = self.category - return context - - -class MediaListView(ListView): - - template_name='media/media_list.html' - context_object_name = 'media' - - def get_queryset(self): - audios = Audio.objects.all() - videos = Video.objects.all() - media_list = [video for video in videos] - # media_list += [audio for audio in audios] - media_list.sort(key=lambda x: x.created_at, reverse=True) - - return media_list - - -class AudioDetailView(SlugMixin, DetailView): - - model = Audio - template_name='festival/video_detail.html' - context_object_name = 'video' + model = Playlist + template_name='media/playlist_detail.html' + context_object_name = 'playlist' def get_context_data(self, **kwargs): - context = super(AudioDetailView, self).get_context_data(**kwargs) + context = super(PlaylistDetailView, self).get_context_data(**kwargs) return context -class AudioListView(ListView): - model = Audio - template_name='festival/video_list.html' +class PlaylistListView(ListView): - def get_queryset(self, **kwargs): - return self.model.objects.published() + template_name='media/playlist_list.html' + context_object_name = 'playlists' def get_context_data(self, **kwargs): - context = super(AudioListView, self).get_context_data(**kwargs) - context['categories'] = MediaCategory.objects.all() + context = super(PlaylistListView, self).get_context_data(**kwargs) return context -class AudioListCategoryView(AudioListView): - - def get_queryset(self): - self.category = MediaCategory.objects.get(slug=self.kwargs['slug']) - return self.model.objects.filter(category=self.category) - - def get_context_data(self, **kwargs): - context = super(AudioListCategoryView, self).get_context_data(**kwargs) - context['category'] = self.category - return context + # def get_queryset(self): + # audio_playlists = PlaylistAudio.objects.all() + # video_playlists = PlaylistVideo.objects.all() + # playlist_list = [video_playlist for video_playlist in video_playlists] + # playlist_list += [audio_playlist for audio_playlist in audio_playlists] + # playlist_list.sort(key=lambda x: x.created_at, reverse=True) + # return playlist_list diff --git a/app/organization/network/admin.py b/app/organization/network/admin.py index cbcd1bfa..1877178f 100644 --- a/app/organization/network/admin.py +++ b/app/organization/network/admin.py @@ -9,17 +9,12 @@ from organization.network.models import * from organization.network.forms import * from organization.pages.models import * from organization.core.admin import * -from organization.pages.admin import PageImageInline, PageBlockInline, PageAudioInline, PageVideoInline +from organization.pages.admin import PageImageInline, PageBlockInline, PagePlaylistInline -class OrganizationAudioInline(StackedDynamicInlineAdmin): +class OrganizationPlaylistInline(StackedDynamicInlineAdmin): - model = OrganizationAudio - - -class OrganizationVideoInline(StackedDynamicInlineAdmin): - - model = OrganizationVideo + model = OrganizationPlaylist class OrganizationLinkInline(StackedDynamicInlineAdmin): @@ -40,16 +35,16 @@ class OrganizationBlockInline(StackedDynamicInlineAdmin): class OrganizationAdmin(BaseTranslationModelAdmin): model = Organization - inlines = [ OrganizationAudioInline, + inlines = [ OrganizationPlaylistInline, OrganizationImageInline, - OrganizationVideoInline, OrganizationBlockInline, OrganizationLinkInline ] list_display = ['name', 'admin_thumb'] + class DepartmentPageAdmin(PageAdmin): - inlines = [PageImageInline, PageBlockInline, PageAudioInline, PageVideoInline, ] + inlines = [PageImageInline, PageBlockInline, PagePlaylistInline, ] class DepartmentAdmin(BaseTranslationModelAdmin): @@ -65,7 +60,7 @@ class TeamAdmin(BaseTranslationModelAdmin): class TeamPageAdmin(PageAdmin): - inlines = [PageImageInline, PageBlockInline, PageAudioInline, PageVideoInline, ] + inlines = [PageImageInline, PageBlockInline, PagePlaylistInline, ] class PersonAdminBase(BaseTranslationModelAdmin): @@ -79,14 +74,9 @@ class PersonActivityInline(StackedDynamicInlineAdmin): fk_name = 'person' -class PersonAudioInline(StackedDynamicInlineAdmin): - - model = PersonAudio - - -class PersonVideoInline(StackedDynamicInlineAdmin): +class PersonPlaylistInline(StackedDynamicInlineAdmin): - model = PersonVideo + model = PersonPlaylist class PersonLinkInline(StackedDynamicInlineAdmin): @@ -113,10 +103,9 @@ class PersonAdmin(BaseTranslationOrderedModelAdmin): model = Person inlines = [PersonActivityInline, - PersonAudioInline, PersonImageInline, - PersonVideoInline, PersonBlockInline, + PersonPlaylistInline, PersonLinkInline, PersonFileInline ] first_fields = ['last_name', 'first_name', 'title', 'gender', 'user'] diff --git a/app/organization/network/migrations/0038_auto_20161013_1631.py b/app/organization/network/migrations/0038_auto_20161013_1631.py new file mode 100644 index 00000000..69b215dc --- /dev/null +++ b/app/organization/network/migrations/0038_auto_20161013_1631.py @@ -0,0 +1,83 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.10 on 2016-10-13 14:31 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('organization-media', '0006_auto_20161013_1631'), + ('organization-network', '0037_organizationtype_css_class'), + ] + + operations = [ + migrations.CreateModel( + name='OrganizationPlaylist', + fields=[ + ('playlist_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='organization-media.Playlist')), + ('organization', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='playlists', to='organization-network.Organization', verbose_name='organization')), + ], + options={ + 'abstract': False, + }, + bases=('organization-media.playlist',), + ), + migrations.CreateModel( + name='PersonPlaylist', + fields=[ + ('playlist_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='organization-media.Playlist')), + ('person', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='playlists', to='organization-network.Person', verbose_name='person')), + ], + options={ + 'abstract': False, + }, + bases=('organization-media.playlist',), + ), + migrations.RemoveField( + model_name='organizationaudio', + name='audio_ptr', + ), + migrations.RemoveField( + model_name='organizationaudio', + name='organization', + ), + migrations.RemoveField( + model_name='organizationvideo', + name='organization', + ), + migrations.RemoveField( + model_name='organizationvideo', + name='video_ptr', + ), + migrations.RemoveField( + model_name='personaudio', + name='audio_ptr', + ), + migrations.RemoveField( + model_name='personaudio', + name='person', + ), + migrations.RemoveField( + model_name='personvideo', + name='person', + ), + migrations.RemoveField( + model_name='personvideo', + name='video_ptr', + ), + migrations.DeleteModel( + name='OrganizationAudio', + ), + migrations.DeleteModel( + name='OrganizationVideo', + ), + migrations.DeleteModel( + name='PersonAudio', + ), + migrations.DeleteModel( + name='PersonVideo', + ), + ] diff --git a/app/organization/network/models.py b/app/organization/network/models.py index b7028907..01bf36a1 100644 --- a/app/organization/network/models.py +++ b/app/organization/network/models.py @@ -125,14 +125,9 @@ class Organization(Named, Address, URL, AdminThumbRelatedMixin): super(Organization, self).save() -class OrganizationAudio(Audio): +class OrganizationPlaylist(Playlist): - organization = models.ForeignKey(Organization, verbose_name=_('organization'), related_name='audios', blank=True, null=True, on_delete=models.SET_NULL) - - -class OrganizationVideo(Video): - - organization = models.ForeignKey(Organization, verbose_name=_('organization'), related_name='videos', blank=True, null=True, on_delete=models.SET_NULL) + organization = models.ForeignKey(Organization, verbose_name=_('organization'), related_name='playlists', blank=True, null=True, on_delete=models.SET_NULL) class OrganizationLink(Link): @@ -273,14 +268,9 @@ class Person(Displayable, AdminThumbMixin): super(Person, self).save(*args, **kwargs) -class PersonAudio(Audio): - - person = models.ForeignKey(Person, verbose_name=_('person'), related_name='audios', blank=True, null=True, on_delete=models.SET_NULL) - - -class PersonVideo(Video): +class PersonPlaylist(Playlist): - person = models.ForeignKey(Person, verbose_name=_('person'), related_name='videos', blank=True, null=True, on_delete=models.SET_NULL) + person = models.ForeignKey(Person, verbose_name=_('person'), related_name='playlists', blank=True, null=True, on_delete=models.SET_NULL) class PersonLink(Link): diff --git a/app/organization/network/translation.py b/app/organization/network/translation.py index 9b2bd91b..7d763d01 100644 --- a/app/organization/network/translation.py +++ b/app/organization/network/translation.py @@ -45,14 +45,8 @@ class PersonActivityTranslationOptions(TranslationOptions): fields = ('comments',) -@register(PersonAudio) -class PersonAudioTranslationOptions(TranslationOptions): - - pass - - -@register(PersonVideo) -class PersonVideoTranslationOptions(TranslationOptions): +@register(PersonPlaylist) +class PersonPlaylistTranslationOptions(TranslationOptions): pass @@ -81,14 +75,8 @@ class PersonBlockTranslationOptions(TranslationOptions): pass -@register(OrganizationAudio) -class OrganizationAudioTranslationOptions(TranslationOptions): - - pass - - -@register(OrganizationVideo) -class OrganizationVideoTranslationOptions(TranslationOptions): +@register(OrganizationPlaylist) +class OrganizationTranslationOptions(TranslationOptions): pass diff --git a/app/organization/pages/admin.py b/app/organization/pages/admin.py index db5d01c0..cb04bfee 100644 --- a/app/organization/pages/admin.py +++ b/app/organization/pages/admin.py @@ -26,15 +26,9 @@ class PageImageInline(TabularDynamicInlineAdmin): model = PageImage -class PageAudioInline(StackedDynamicInlineAdmin): +class PagePlaylistInline(StackedDynamicInlineAdmin): - model = PageAudio - exclude = ("short_url", "keywords", "description", "slug", ) - - -class PageVideoInline(StackedDynamicInlineAdmin): - - model = PageVideo + model = PagePlaylist exclude = ("short_url", "keywords", "description", "slug", ) @@ -79,8 +73,7 @@ class CustomPageAdmin(PageAdmin): inlines = [PageBlockInline, PageImageInline, - PageAudioInline, - PageVideoInline, + PagePlaylistInline, PageLinkInline, PersonListBlockAutocompleteInlineAdmin, PageProductListInline, diff --git a/app/organization/pages/forms.py b/app/organization/pages/forms.py index e82ada1f..a8a2d7d5 100644 --- a/app/organization/pages/forms.py +++ b/app/organization/pages/forms.py @@ -11,7 +11,8 @@ from organization.magazine.models import Article, Topic, Brief from organization.pages.models import CustomPage from organization.pages.models import * from organization.agenda.models import Event -from organization.media.models import Audio, Video +from organization.media.models import Playlist + class DynamicContentHomeSliderForm(autocomplete.FutureModelForm): @@ -52,8 +53,7 @@ class DynamicContentHomeMediaForm(autocomplete.FutureModelForm): content_object = dal_queryset_sequence.fields.QuerySetSequenceModelField( queryset=autocomplete.QuerySetSequence( - Audio.objects.all(), - Video.objects.all(), + Playlist.objects.all(), ), required=False, widget=dal_select2_queryset_sequence.widgets.QuerySetSequenceSelect2('dynamic-content-home-media'), diff --git a/app/organization/pages/migrations/0008_auto_20161013_1631.py b/app/organization/pages/migrations/0008_auto_20161013_1631.py new file mode 100644 index 00000000..9734838c --- /dev/null +++ b/app/organization/pages/migrations/0008_auto_20161013_1631.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.10 on 2016-10-13 14:31 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('organization-media', '0006_auto_20161013_1631'), + ('pages', '0005_auto_20160923_1219'), + ('organization-pages', '0007_auto_20161007_1852'), + ] + + operations = [ + migrations.CreateModel( + name='PagePlaylist', + fields=[ + ('playlist_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='organization-media.Playlist')), + ('page', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='playlists', to='pages.Page', verbose_name='page')), + ], + options={ + 'verbose_name': 'playlist', + 'verbose_name_plural': 'playlists', + }, + bases=('organization-media.playlist',), + ), + migrations.DeleteModel( + name='PageAudio', + ), + migrations.DeleteModel( + name='PageVideo', + ), + migrations.AlterOrderWithRespectTo( + name='pageplaylist', + order_with_respect_to='page', + ), + ] diff --git a/app/organization/pages/models.py b/app/organization/pages/models.py index 02047b19..b9b8d5b3 100644 --- a/app/organization/pages/models.py +++ b/app/organization/pages/models.py @@ -33,23 +33,13 @@ class PageImage(Image): order_with_respect_to = "page" -class PageAudio(Audio): +class PagePlaylist(Playlist): - page = models.ForeignKey(Page, verbose_name=_('page'), related_name='audios', blank=True, null=True, on_delete=models.SET_NULL) + page = models.ForeignKey(Page, verbose_name=_('page'), related_name='playlists', blank=True, null=True, on_delete=models.SET_NULL) class Meta: - verbose_name = _("audio") - verbose_name_plural = _("audios") - order_with_respect_to = "page" - - -class PageVideo(Video): - - page = models.ForeignKey(Page, verbose_name=_('page'), related_name='videos', blank=True, null=True, on_delete=models.SET_NULL) - - class Meta: - verbose_name = _("video") - verbose_name_plural = _("videos") + verbose_name = _("playlist") + verbose_name_plural = _("playlists") order_with_respect_to = "page" diff --git a/app/organization/pages/translation.py b/app/organization/pages/translation.py index d6863648..79010de9 100644 --- a/app/organization/pages/translation.py +++ b/app/organization/pages/translation.py @@ -46,14 +46,8 @@ class PageImageTranslationOptions(TranslationOptions): fields = ('description',) -@register(PageVideo) -class PageVideoTranslationOptions(TranslationOptions): - - pass - - -@register(PageAudio) -class PageAudioTranslationOptions(TranslationOptions): +@register(PagePlaylist) +class PagePlaylistTranslationOptions(TranslationOptions): pass diff --git a/app/organization/pages/views.py b/app/organization/pages/views.py index 384acf54..2f5ac05d 100644 --- a/app/organization/pages/views.py +++ b/app/organization/pages/views.py @@ -11,7 +11,7 @@ from organization.core.views import SlugMixin from organization.magazine.models import Article, Topic, Brief from organization.pages.models import Home from organization.agenda.models import Event -from organization.media.models import Audio, Video +from organization.media.models import Playlist class HomeView(SlugMixin, ListView): @@ -91,14 +91,12 @@ class DynamicContentHomeMediaView(Select2QuerySetSequenceView): def get_queryset(self): - videos = Video.objects.all() - audios = Audio.objects.all() + playlists = Playlist.objects.all() if self.q: - videos = videos.filter(title__icontains=self.q) - audios = audios.filter(title__icontains=self.q) + playlists = videos.filter(title__icontains=self.q) - qs = autocomplete.QuerySetSequence(videos, audios,) + qs = autocomplete.QuerySetSequence(playlists,) if self.q: qs = qs.filter(title__icontains=self.q) diff --git a/app/organization/projects/admin.py b/app/organization/projects/admin.py index e2932d26..b217e546 100644 --- a/app/organization/projects/admin.py +++ b/app/organization/projects/admin.py @@ -8,7 +8,7 @@ from mezzanine.pages.admin import PageAdmin from organization.projects.models import * from organization.pages.models import * -from organization.media.models import Video, Audio +from organization.media.models import Playlist class ProjectLinkInline(StackedDynamicInlineAdmin): @@ -26,14 +26,9 @@ class ProjectBlockInline(StackedDynamicInlineAdmin): model = ProjectBlock -class ProjectAudioInline(StackedDynamicInlineAdmin): +class ProjectPlaylistInline(StackedDynamicInlineAdmin): - model = ProjectAudio - - -class ProjectVideoInline(StackedDynamicInlineAdmin): - - model = ProjectVideo + model = ProjectPlaylist class ProjectFileInline(TabularDynamicInlineAdmin): @@ -51,8 +46,7 @@ class ProjectAdminDisplayable(DisplayableAdmin): fieldsets = deepcopy(ProjectAdmin.fieldsets) inlines = [ ProjectBlockInline, ProjectImageInline, - ProjectAudioInline, - ProjectVideoInline, + ProjectPlaylistInline, ProjectLinkInline, ProjectFileInline] filter_horizontal = ['teams', 'organizations'] diff --git a/app/organization/projects/migrations/0020_auto_20161013_1631.py b/app/organization/projects/migrations/0020_auto_20161013_1631.py new file mode 100644 index 00000000..3c386819 --- /dev/null +++ b/app/organization/projects/migrations/0020_auto_20161013_1631.py @@ -0,0 +1,50 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.10 on 2016-10-13 14:31 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('organization-media', '0006_auto_20161013_1631'), + ('organization-projects', '0019_auto_20161007_1045'), + ] + + operations = [ + migrations.CreateModel( + name='ProjectPlaylist', + fields=[ + ('playlist_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='organization-media.Playlist')), + ('project', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='playlists', to='organization-projects.Project', verbose_name='project')), + ], + options={ + 'abstract': False, + }, + bases=('organization-media.playlist',), + ), + migrations.RemoveField( + model_name='projectaudio', + name='audio_ptr', + ), + migrations.RemoveField( + model_name='projectaudio', + name='project', + ), + migrations.RemoveField( + model_name='projectvideo', + name='project', + ), + migrations.RemoveField( + model_name='projectvideo', + name='video_ptr', + ), + migrations.DeleteModel( + name='ProjectAudio', + ), + migrations.DeleteModel( + name='ProjectVideo', + ), + ] diff --git a/app/organization/projects/models.py b/app/organization/projects/models.py index 5666539b..38d14adb 100644 --- a/app/organization/projects/models.py +++ b/app/organization/projects/models.py @@ -79,14 +79,9 @@ class ProjectProgramType(Named): ordering = ['name',] -class ProjectAudio(Audio): +class ProjectPlaylist(Playlist): - project = models.ForeignKey(Project, verbose_name=_('project'), related_name='audios', blank=True, null=True, on_delete=models.SET_NULL) - - -class ProjectVideo(Video): - - project = models.ForeignKey(Project, verbose_name=_('project'), related_name='videos', blank=True, null=True, on_delete=models.SET_NULL) + project = models.ForeignKey(Project, verbose_name=_('project'), related_name='playlists', blank=True, null=True, on_delete=models.SET_NULL) class ProjectLink(Link): diff --git a/app/organization/projects/translation.py b/app/organization/projects/translation.py index fea7a757..6183b36a 100644 --- a/app/organization/projects/translation.py +++ b/app/organization/projects/translation.py @@ -9,14 +9,8 @@ class ProjectTranslationOptions(TranslationOptions): fields = ('title', 'description', 'content') -@register(ProjectVideo) -class ProjectVideoTranslationOptions(TranslationOptions): - - pass - - -@register(ProjectAudio) -class ProjectAudioTranslationOptions(TranslationOptions): +@register(ProjectPlaylist) +class ProjectPlaylistTranslationOptions(TranslationOptions): pass diff --git a/app/organization/shop/admin.py b/app/organization/shop/admin.py index cc66060d..fb5f2eb0 100644 --- a/app/organization/shop/admin.py +++ b/app/organization/shop/admin.py @@ -8,7 +8,6 @@ from mezzanine.core.admin import * from organization.core.admin import * from organization.projects.models import * from organization.pages.models import * -from organization.media.models import Video, Audio from organization.shop.models import * from cartridge.shop.admin import * -- 2.39.5