Commit 44016a34 authored by fred's avatar fred

switch to standalone emissions app

parent 62eec5e6
...@@ -4,7 +4,7 @@ from django.contrib.auth.models import AbstractUser ...@@ -4,7 +4,7 @@ from django.contrib.auth.models import AbstractUser
from django.core import validators from django.core import validators
from django.db import models from django.db import models
from ..emissions.models import Emission from emissions.models import Emission
class User(AbstractUser): class User(AbstractUser):
emissions = models.ManyToManyField(Emission) emissions = models.ManyToManyField(Emission)
......
from django.contrib import admin
from .models import Emission, Episode, Category, Schedule, \
SoundFile, NewsItem, NewsCategory, Nonstop
class EmissionAdmin(admin.ModelAdmin):
prepopulated_fields = {'slug': ('title',)}
admin.site.register(Emission, EmissionAdmin)
class EpisodeAdmin(admin.ModelAdmin):
prepopulated_fields = {'slug': ('title',)}
admin.site.register(Episode, EpisodeAdmin)
class CategoryAdmin(admin.ModelAdmin):
pass
admin.site.register(Category, CategoryAdmin)
class ScheduleAdmin(admin.ModelAdmin):
pass
admin.site.register(Schedule, ScheduleAdmin)
class NewsCategoryAdmin(admin.ModelAdmin):
pass
admin.site.register(NewsCategory, NewsCategoryAdmin)
class NewsItemAdmin(admin.ModelAdmin):
pass
admin.site.register(NewsItem, NewsItemAdmin)
class SoundFileAdmin(admin.ModelAdmin):
pass
admin.site.register(SoundFile, SoundFileAdmin)
class NonstopAdmin(admin.ModelAdmin):
pass
admin.site.register(Nonstop, NonstopAdmin)
[
{
"fields": {
"end": "02:00:00",
"start": "22:00:00",
"title": "Biodiversit\u00e9"
},
"model": "emissions.nonstop",
"pk": 1
},
{
"fields": {
"end": "05:00:00",
"start": "02:00:00",
"title": "R\u00eaveries"
},
"model": "emissions.nonstop",
"pk": 2
},
{
"fields": {
"end": "07:30:00",
"start": "05:00:00",
"title": "La Panique"
},
"model": "emissions.nonstop",
"pk": 3
},
{
"fields": {
"end": "10:00:00",
"start": "07:30:00",
"title": "Matin tranquille"
},
"model": "emissions.nonstop",
"pk": 4
},
{
"fields": {
"end": "12:00:00",
"start": "10:00:00",
"title": "Up Beat Tempo"
},
"model": "emissions.nonstop",
"pk": 5
},
{
"fields": {
"end": "13:00:00",
"start": "12:00:00",
"title": "L'heure de pointe"
},
"model": "emissions.nonstop",
"pk": 6
},
{
"fields": {
"end": "16:00:00",
"start": "13:00:00",
"title": "Le Mange Disque"
},
"model": "emissions.nonstop",
"pk": 7
},
{
"fields": {
"end": "19:00:00",
"start": "16:00:00",
"title": "Hop Bop and co"
},
"model": "emissions.nonstop",
"pk": 8
},
{
"fields": {
"end": "22:00:00",
"start": "19:00:00",
"title": "Acouph\u00e8ne"
},
"model": "emissions.nonstop",
"pk": 9
}
]
import datetime
import re
import unicodedata
import os
import uuid
from django import forms
from django.forms import fields
from django.core.files.storage import DefaultStorage
from django.core.urlresolvers import reverse
from django.utils.safestring import mark_safe
from django.conf import settings
from django.template.loader import render_to_string
from taggit.forms import TagWidget
import datetimewidget.widgets
from .models import Emission, Episode, Diffusion, Schedule, SoundFile
DATETIME_OPTIONS = {
'format': 'dd/mm/yyyy hh:ii',
'language': 'fr',
'weekStart': '1',
'autoclose': 'true',
}
class DateTimeWidget(datetimewidget.widgets.DateTimeWidget):
def __init__(self, *args, **kwargs):
super(DateTimeWidget, self).__init__(*args, options=DATETIME_OPTIONS, **kwargs)
def slugify(s):
s = unicodedata.normalize('NFKD', s).encode('ascii', 'ignore').lower()
return re.sub(r'\W+', '-', s)
class DayAndHourWidget(forms.MultiWidget):
def __init__(self, attrs=None):
WEEKDAYS = [u'Lundi', u'Mardi', u'Mercredi', u'Jeudi', u'Vendredi', u'Samedi', u'Dimanche']
widgets = (
forms.Select(attrs=attrs, choices=([(weekday, WEEKDAYS[weekday]) for weekday in range(7)])),
forms.Select(attrs=attrs, choices=([(hour, hour) for hour in range(24)])),
forms.Select(attrs=attrs, choices=([(minute, str(minute).zfill(2)) for minute in range(60)])),
)
super(DayAndHourWidget, self).__init__(widgets, attrs)
def decompress(self, value):
if value:
return [value.weekday(), value.hour, value.minute]
return [None, None, None]
def value_from_datadict(self, data, files, name):
# we only care about day/hour/minutes, but we conveniently use a
# datetime value to store that; we pick 2007 as reference year as
# it had its January 1st on a Monday.
data_list = [
widget.value_from_datadict(data, files, name + '_%s' % i)
for i, widget in enumerate(self.widgets)]
if data_list:
return datetime.datetime(2007, 1, int(data_list[0])+1, int(data_list[1]), int(data_list[2]))
return None
class JqueryFileUploadFileInput(forms.FileInput):
def render(self, name, value, attrs=None):
output = render_to_string('emissions/upload.html', {
'upload_url': self.url,
'files': self.files,
'name': name,
'STATIC_URL': settings.STATIC_URL})
return mark_safe(output)
class JqueryFileUploadInput(forms.MultiWidget):
needs_multipart_form = True
upload_id_re = re.compile(r'^[a-z0-9A-Z-]+$')
upload_id = None
def __init__(self, attrs=None, choices=[], max_filename_length=None):
self.max_filename_length = max_filename_length
widget_list = (forms.HiddenInput(attrs=attrs),
JqueryFileUploadFileInput(attrs=attrs))
super(JqueryFileUploadInput, self).__init__(widget_list, attrs)
def decompress(self, value):
# map python value to widget contents
if self.upload_id:
pass
elif isinstance(value, (list, tuple)) and value and value[0] is not None:
self.upload_id = str(value[0])
else:
self.upload_id = str(uuid.uuid4())
return [self.upload_id, None]
def get_files_for_id(self, upload_id):
storage = DefaultStorage()
path = os.path.join('upload', upload_id)
if not storage.exists(path):
return
for filepath in storage.listdir(path)[1]:
name = os.path.basename(filepath)
yield storage.open(os.path.join(path, name))
def value_from_datadict(self, data, files, name):
'''
If some file was submitted, that's the value,
If a regular hidden_id is present, use it to find uploaded files,
otherwise return an empty list
'''
upload_id, file_input = super(JqueryFileUploadInput, self).value_from_datadict(data, files, name)
if file_input:
pass
elif JqueryFileUploadInput.upload_id_re.match(upload_id):
file_input = list(self.get_files_for_id(upload_id))
else:
file_input = []
return file_input[0]
def render(self, name, value, attrs=None):
self.decompress(value)
self.widgets[1].url = '/upload/%s/' % self.upload_id
self.widgets[1].url = reverse('upload', kwargs={'transaction_id': self.upload_id})
if self.max_filename_length:
self.widgets[1].url += '?max_filename_length=%d' % self.max_filename_length
self.widgets[1].files = '/upload/%s/' % self.get_files_for_id(self.upload_id)
output = super(JqueryFileUploadInput, self).render(name, value,
attrs)
fileinput_id = '%s_%s' % (attrs['id'], '1')
return output
class EmissionForm(forms.ModelForm):
class Meta:
model = Emission
exclude = ('slug',)
def save(self, commit=True):
if not self.instance.slug:
self.instance.slug = slugify(self.instance.title)
return super(EmissionForm, self).save(commit=commit)
class EpisodeForm(forms.ModelForm):
class Meta:
model = Episode
exclude = ('slug',)
widgets = {'emission': forms.HiddenInput(),
'tags': TagWidget()}
def save(self, commit=True):
if not self.instance.slug:
self.instance.slug = slugify(self.instance.title)
return super(EpisodeForm, self).save(commit=commit)
class EpisodeNewForm(EpisodeForm):
diffusion = forms.DateTimeField(label='First Diffusion',
widget=DateTimeWidget)
def save(self, commit=True):
episode = super(EpisodeNewForm, self).save(commit=commit)
diffusion = Diffusion()
diffusion.episode_id = episode.id
diffusion.datetime = self.cleaned_data.get('diffusion')
diffusion.save()
return episode
class ScheduleForm(forms.ModelForm):
class Meta:
model = Schedule
widgets = {
'emission': forms.HiddenInput(),
'datetime': DayAndHourWidget(),
}
class SoundFileForm(forms.ModelForm):
class Meta:
model = SoundFile
widgets = {
'episode': forms.HiddenInput(),
'file': JqueryFileUploadInput(),
}
class DiffusionForm(forms.ModelForm):
class Meta:
model = Diffusion
widgets = {
'episode': forms.HiddenInput(),
'datetime': DateTimeWidget(),
}
This diff is collapsed.
import base64
import mutagen
import mutagen.mp3
import os
import subprocess
from optparse import make_option
from django.core.management.base import BaseCommand, CommandError
from ...models import SoundFile
class Command(BaseCommand):
option_list = BaseCommand.option_list + (
make_option('--force',
action='store_true',
dest='force',
default=False,
help='Create files even if they exist'),
make_option('--reset-metadata',
action='store_true',
dest='reset_metadata',
default=False,
help='Reset metadata on all files'),
make_option('--emission',
dest='emission',
metavar='EMISSION',
default=None,
help='Process files belonging to emission only'),
make_option('--episode',
dest='episode',
metavar='EPISODE',
default=None,
help='Process files belonging to episode only'),
)
def handle(self, force, reset_metadata, emission, episode, verbosity, **kwargs):
self.verbose = (verbosity > 1)
for soundfile in SoundFile.objects.select_related().exclude(podcastable=False):
if emission and soundfile.episode.emission.slug != emission:
continue
if episode and soundfile.episode.slug != episode:
continue
if soundfile.file is None or not os.path.exists(soundfile.file.path):
continue
for format in ('ogg', 'mp3'):
file_path = soundfile.get_format_path(format)
if not os.path.exists(file_path) or force:
self.create(soundfile, format)
if force or reset_metadata:
self.set_metadata(soundfile, format)
def create(self, soundfile, format):
file_path = soundfile.get_format_path(format)
cmd = ['sox', soundfile.file.path, file_path]
if format == 'ogg':
cmd[-1:-1] = ['-C', '4'] # q4 (~128kbps)
elif format == 'mp3':
cmd[-1:-1] = ['-C', '-4'] # vbr @ ~128kbps
if self.verbose:
print 'creating', file_path
cmd[1:1] = ['--show-progress']
print ' ', ' '.join(cmd)
subprocess.call(cmd)
def set_metadata(self, soundfile, format):
file_path = soundfile.get_format_path(format)
if format == 'mp3':
audio = mutagen.mp3.MP3(file_path, ID3=mutagen.easyid3.EasyID3)
elif format == 'ogg':
audio = mutagen.File(file_path)
if 'comment' in audio:
del audio['comment']
if soundfile.fragment is True and soundfile.title:
audio['title'] = '%s - %s' % (
soundfile.episode.title,
soundfile.title)
else:
audio['title'] = soundfile.episode.title
audio['album'] = soundfile.episode.emission.title
audio['artist'] = 'Radio Panik'
if soundfile.episode.image or soundfile.episode.emission.image:
image = (soundfile.episode.image or soundfile.episode.emission.image)
image.file.open()
if os.path.splitext(image.path)[1].lower() in ('.jpeg', '.jpg'):
mimetype = 'image/jpeg'
elif os.path.splitext(image.path)[1].lower() == '.png':
mimetype = 'image/png'
else:
mimetype = None
if mimetype:
if format == 'ogg':
audio['coverartmime'] = mimetype
audio['coverartdescription'] = 'image'
audio['coverart'] = base64.encodestring(image.read()).replace('\n', '')
elif format == 'mp3':
audio.save()
audio = mutagen.mp3.MP3(file_path)
audio.tags.add(mutagen.id3.APIC(
encoding=3, description='image',
type=3, mime=mimetype, data=image.read()))
image.close()
audio.save()
from datetime import datetime
from django.core.management.base import BaseCommand, CommandError
from ...utils import day_program
class Command(BaseCommand):
def handle(self, year, month, day, **options):
date = datetime(int(year), int(month), int(day))
for entry in day_program(date):
print entry.datetime, entry
from datetime import datetime, timedelta
from django.core.management.base import BaseCommand, CommandError
from django.views.generic.dates import _date_from_string
from ...utils import period_program
class Command(BaseCommand):
def handle(self, year, week, **options):
date = _date_from_string(year, '%Y',
'1', '%w',
week, '%W')
date = datetime(*date.timetuple()[:3])
for entry in period_program(date, date+timedelta(days=7)):
print entry.datetime, entry
from django.core.management.base import BaseCommand, CommandError
from ...utils import whatsonair
class Command(BaseCommand):
def handle(self, *args, **options):
onair = whatsonair()
print 'found episode:', onair.get('episode')
print 'found emission:', onair.get('emission')
print 'found nonstop:', onair.get('nonstop')
# -*- coding: utf-8 -*-
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding model 'Category'
db.create_table(u'emissions_category', (
(u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('title', self.gf('django.db.models.fields.CharField')(max_length=50)),
))
db.send_create_signal(u'emissions', ['Category'])
# Adding model 'Emission'
db.create_table(u'emissions_emission', (
(u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('title', self.gf('django.db.models.fields.CharField')(max_length=50)),
('slug', self.gf('django.db.models.fields.SlugField')(max_length=50)),
('description', self.gf('ckeditor.fields.RichTextField')(null=True, blank=True)),
('text', self.gf('ckeditor.fields.RichTextField')(null=True)),
('archived', self.gf('django.db.models.fields.BooleanField')(default=False)),
))
db.send_create_signal(u'emissions', ['Emission'])
# Adding M2M table for field categories on 'Emission'
m2m_table_name = db.shorten_name(u'emissions_emission_categories')
db.create_table(m2m_table_name, (
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
('emission', models.ForeignKey(orm[u'emissions.emission'], null=False)),
('category', models.ForeignKey(orm[u'emissions.category'], null=False))
))
db.create_unique(m2m_table_name, ['emission_id', 'category_id'])
# Adding model 'Schedule'
db.create_table(u'emissions_schedule', (
(u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('emission', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['emissions.Emission'])),
('datetime', self.gf('django.db.models.fields.DateTimeField')()),
('rerun', self.gf('django.db.models.fields.BooleanField')(default=False)),
))
db.send_create_signal(u'emissions', ['Schedule'])
# Adding model 'Episode'
db.create_table(u'emissions_episode', (
(u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('emission', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['emissions.Emission'])),
('title', self.gf('django.db.models.fields.CharField')(max_length=50)),
('slug', self.gf('django.db.models.fields.SlugField')(max_length=50)),
('description', self.gf('ckeditor.fields.RichTextField')(null=True, blank=True)),
('text', self.gf('ckeditor.fields.RichTextField')(null=True)),
))
db.send_create_signal(u'emissions', ['Episode'])
# Adding model 'Diffusion'
db.create_table(u'emissions_diffusion', (
(u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('episode', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['emissions.Episode'])),
('datetime', self.gf('django.db.models.fields.DateTimeField')()),
))
db.send_create_signal(u'emissions', ['Diffusion'])
# Adding model 'SoundFile'
db.create_table(u'emissions_soundfile', (
(u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('episode', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['emissions.Episode'])),
('file', self.gf('django.db.models.fields.files.FileField')(max_length=250)),
('podcastable', self.gf('django.db.models.fields.BooleanField')(default=False)),
('fragment', self.gf('django.db.models.fields.BooleanField')(default=False)),
('title', self.gf('django.db.models.fields.CharField')(max_length=50)),
))
db.send_create_signal(u'emissions', ['SoundFile'])
def backwards(self, orm):
# Deleting model 'Category'
db.delete_table(u'emissions_category')
# Deleting model 'Emission'
db.delete_table(u'emissions_emission')
# Removing M2M table for field categories on 'Emission'
db.delete_table(db.shorten_name(u'emissions_emission_categories'))
# Deleting model 'Schedule'
db.delete_table(u'emissions_schedule')
# Deleting model 'Episode'
db.delete_table(u'emissions_episode')
# Deleting model 'Diffusion'
db.delete_table(u'emissions_diffusion')
# Deleting model 'SoundFile'
db.delete_table(u'emissions_soundfile')
models = {
u'emissions.category': {
'Meta': {'object_name': 'Category'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'title': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
u'emissions.diffusion': {
'Meta': {'object_name': 'Diffusion'},
'datetime': ('django.db.models.fields.DateTimeField', [], {}),
'episode': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['emissions.Episode']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
u'emissions.emission': {
'Meta': {'object_name': 'Emission'},
'archived': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'categories': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['emissions.Category']", 'symmetrical': 'False'}),
'description': ('ckeditor.fields.RichTextField', [], {'null': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50'}),
'text': ('ckeditor.fields.RichTextField', [], {'null': 'True'}),
'title': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
u'emissions.episode': {
'Meta': {'object_name': 'Episode'},
'description': ('ckeditor.fields.RichTextField', [], {'null': 'True', 'blank': 'True'}),
'emission': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['emissions.Emission']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50'}),
'text': ('ckeditor.fields.RichTextField', [], {'null': 'True'}),
'title': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
u'emissions.schedule': {
'Meta': {'object_name': 'Schedule'},
'datetime': ('django.db.models.fields.DateTimeField', [], {}),
'emission': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['emissions.Emission']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'rerun': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
},
u'emissions.soundfile': {
'Meta': {'object_name': 'SoundFile'},
'episode': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['emissions.Episode']"}),
'file': ('django.db.models.fields.files.FileField', [], {'max_length': '250'}),
'fragment': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'podcastable': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'title': ('django.db.models.fields.CharField', [], {'max_length': '50'})
}
}
complete_apps = ['emissions']
\ No newline at end of file
# -*- coding: utf-8 -*-
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding field 'Emission.duration'
db.add_column(u'emissions_emission', 'duration',
self.gf('django.db.models.fields.IntegerField')(default=60),
keep_default=False)
def backwards(self, orm):
# Deleting field 'Emission.duration'
db.delete_column(u'emissions_emission', 'duration')
models = {
u'emissions.category': {
'Meta': {'object_name': 'Category'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),