7 from django import forms
8 from django.forms import fields
9 from django.forms import ValidationError
11 from django.core.files.storage import DefaultStorage
12 from django.core.urlresolvers import reverse
13 from django.utils.safestring import mark_safe
14 from django.utils.translation import ugettext_lazy as _
15 from django.utils.encoding import force_text
16 from django.conf import settings
17 from django.template.loader import render_to_string
19 from taggit.forms import TagWidget
21 from .models import (Emission, Episode, Diffusion, Schedule, SoundFile,
22 NewsItem, Absence, PlaylistElement)
23 from .utils import get_duration
24 from .widgets import DateTimeWidget, DateWidget
28 s = unicodedata.normalize('NFKD', s).encode('ascii', 'ignore').lower()
29 return re.sub(r'\W+', '-', force_text(s))
32 class DayAndHourWidget(forms.MultiWidget):
33 def __init__(self, attrs=None):
34 WEEKDAYS = [_('Monday'), _('Tuesday'), _('Wednesday'), _('Thursday'),
35 _('Friday'), _('Saturday'), _('Sunday')]
37 forms.Select(attrs=attrs, choices=([(weekday, WEEKDAYS[weekday]) for weekday in range(7)])),
38 forms.Select(attrs=attrs, choices=([(hour, hour) for hour in range(24)])),
39 forms.Select(attrs=attrs, choices=([(minute, str(minute).zfill(2)) for minute in range(60)])),
41 super(DayAndHourWidget, self).__init__(widgets, attrs)
43 def decompress(self, value):
45 return [value.weekday(), value.hour, value.minute]
46 return [None, None, None]
48 def value_from_datadict(self, data, files, name):
49 # we only care about day/hour/minutes, but we conveniently use a
50 # datetime value to store that; we pick 2007 as reference year as
51 # it had its January 1st on a Monday.
53 widget.value_from_datadict(data, files, name + '_%s' % i)
54 for i, widget in enumerate(self.widgets)]
57 return datetime.datetime(2007, 1, int(data_list[0])+1, int(data_list[1]), int(data_list[2]))
61 class JqueryFileUploadFileInput(forms.FileInput):
62 template_name = 'emissions/upload.html'
64 def get_context(self, name, value, attrs):
65 context = super(JqueryFileUploadFileInput, self).get_context(name, value, attrs)
66 context['widget'].update({
67 'upload_url': self.url,
69 'STATIC_URL': settings.STATIC_URL}
73 def render(self, name, value, attrs=None):
74 output = render_to_string('emissions/upload.html', {'widget': {
75 'upload_url': self.url,
78 'STATIC_URL': settings.STATIC_URL}})
79 return mark_safe(output)
82 class JqueryFileUploadInput(forms.MultiWidget):
83 needs_multipart_form = True
84 upload_id_re = re.compile(r'^[a-z0-9A-Z-]+$')
87 def __init__(self, attrs=None, choices=[], max_filename_length=None):
88 self.max_filename_length = max_filename_length
89 widget_list = (forms.HiddenInput(attrs=attrs),
90 JqueryFileUploadFileInput(attrs=attrs))
91 super(JqueryFileUploadInput, self).__init__(widget_list, attrs)
93 def decompress(self, value):
94 # map python value to widget contents
97 elif isinstance(value, (list, tuple)) and value and value[0] is not None:
98 self.upload_id = str(value[0])
100 self.upload_id = str(uuid.uuid4())
101 return [self.upload_id, None]
103 def get_files_for_id(self, upload_id):
104 storage = DefaultStorage()
105 path = os.path.join('upload', upload_id)
106 if not storage.exists(path):
108 for filepath in storage.listdir(path)[1]:
109 name = os.path.basename(filepath)
110 yield storage.open(os.path.join(path, name))
112 def value_from_datadict(self, data, files, name):
114 If some file was submitted, that's the value,
115 If a regular hidden_id is present, use it to find uploaded files,
116 otherwise return an empty list
118 upload_id, file_input = super(JqueryFileUploadInput, self).value_from_datadict(data, files, name)
121 elif JqueryFileUploadInput.upload_id_re.match(upload_id):
122 file_input = list(self.get_files_for_id(upload_id))
130 def render(self, name, value, attrs=None):
131 self.decompress(value)
132 self.widgets[1].url = '/upload/%s/' % self.upload_id
133 self.widgets[1].url = reverse('upload', kwargs={'transaction_id': self.upload_id})
134 if self.max_filename_length:
135 self.widgets[1].url += '?max_filename_length=%d' % self.max_filename_length
136 self.widgets[1].files = '/upload/%s/' % self.get_files_for_id(self.upload_id)
137 output = super(JqueryFileUploadInput, self).render(name, value, attrs)
138 fileinput_id = '%s_%s' % (attrs['id'], '1')
142 class EmissionForm(forms.ModelForm):
145 exclude = ('slug', 'colours', 'has_focus', 'got_focus', 'chat_open', 'podcast_sound_quality')
147 def save(self, commit=True):
148 if not self.instance.slug:
149 self.instance.slug = slugify(self.instance.title)
150 return super(EmissionForm, self).save(commit=commit)
153 class EpisodeForm(forms.ModelForm):
156 exclude = ('slug', 'has_focus', 'got_focus', 'effective_start', 'effective_end')
157 widgets = {'emission': forms.HiddenInput(),
160 def save(self, commit=True):
161 if not self.instance.slug:
162 base_slug = slugify(self.instance.title)
167 Episode.objects.get(slug=slug)
168 except Episode.DoesNotExist:
171 slug = '%s-%s' % (base_slug, i)
173 self.instance.slug = slug
174 return super(EpisodeForm, self).save(commit=commit)
177 class EpisodeNewForm(EpisodeForm):
178 first_diffusion = forms.DateTimeField(label=_('First Diffusion'),
179 widget=DateTimeWidget)
180 second_diffusion = forms.DateTimeField(label=_('Second Diffusion'),
181 widget=DateTimeWidget)
182 third_diffusion = forms.DateTimeField(label=_('Third Diffusion'),
183 widget=DateTimeWidget)
184 fourth_diffusion = forms.DateTimeField(label=_('Fourth Diffusion'),
185 widget=DateTimeWidget)
187 def get_diffusion_fields(self, emission):
189 schedules = list(Schedule.objects.filter(emission=emission).order_by('datetime'))
190 if len(schedules) > 1 and (
191 schedules[0].weeks == schedules[1].weeks and
192 schedules[0].datetime.date() == schedules[1].datetime.date()):
193 # special case for daily program with same-day rerun
196 schedules = len([x for x in schedules if x.rerun]) + 1
199 fields = ['first_diffusion']
201 fields.append('second_diffusion')
203 fields.append('third_diffusion')
205 fields.append('fourth_diffusion')
208 def __init__(self, *args, **kwargs):
209 super(EpisodeNewForm, self).__init__(*args, **kwargs)
210 emission = kwargs.get('initial', {}).get('emission')
211 diffusion_fields = self.get_diffusion_fields(emission)
212 for field_name in list(self.fields.keys()):
213 if field_name.endswith('_diffusion') and not field_name in diffusion_fields:
214 del self.fields[field_name]
216 def save(self, commit=True):
217 episode = super(EpisodeNewForm, self).save(commit=commit)
218 for field_name in self.get_diffusion_fields(episode.emission):
219 diffusion = Diffusion()
220 diffusion.episode_id = episode.id
221 diffusion.datetime = self.cleaned_data.get(field_name)
226 class ScheduleForm(forms.ModelForm):
231 'emission': forms.HiddenInput(),
232 'datetime': DayAndHourWidget(),
236 class SoundFileForm(forms.ModelForm):
239 exclude = ('has_focus', 'got_focus', 'duration')
241 'episode': forms.HiddenInput(),
242 'file': JqueryFileUploadInput(),
247 if self.cleaned_data.get('file'):
248 duration = get_duration(self.cleaned_data['file'].file.name)
250 raise ValidationError(_('Invalid file, could not get duration.'))
253 class SoundFileEditForm(forms.ModelForm):
256 exclude = ('has_focus', 'got_focus', 'file', 'duration')
258 'episode': forms.HiddenInput(),
261 def __init__(self, *args, **kwargs):
262 super(SoundFileEditForm, self).__init__(*args, **kwargs)
263 self.fields.keyOrder = ['title', 'format', 'podcastable', 'fragment', 'license']
266 class DiffusionForm(forms.ModelForm):
271 'episode': forms.HiddenInput(),
272 'datetime': DateTimeWidget(),
276 class NewsItemForm(forms.ModelForm):
279 exclude = ('slug', 'has_focus', 'got_focus')
280 widgets = {'tags': TagWidget(),
281 'date': DateWidget(),
282 'expiration_date': DateWidget(),
283 'event_date': DateWidget(),
286 def __init__(self, *args, **kwargs):
287 super(NewsItemForm, self).__init__(*args, **kwargs)
288 user = kwargs.get('initial', {}).get('user')
289 if not user.is_staff:
290 self.fields['emission'].widget = forms.HiddenInput()
292 def save(self, commit=True):
293 if not self.instance.slug:
294 base_slug = slugify(self.instance.title)
299 NewsItem.objects.get(slug=slug)
300 except NewsItem.DoesNotExist:
303 slug = '%s-%s' % (base_slug, i)
305 self.instance.slug = slug
306 return super(NewsItemForm, self).save(commit=commit)
309 class AbsenceDateTimeWidget(forms.Select):
310 def __init__(self, emission, *args, **kwargs):
311 super().__init__(*args, **kwargs)
312 self.choices = list(self.options(emission))
314 def options(self, emission):
318 since = dates[-1] + datetime.timedelta(minutes=10)
321 dates.append(emission.get_next_planned_date(since))
323 yield (date, date.strftime('%d/%m/%Y %H:%M'))
326 class AbsenceForm(forms.ModelForm):
331 'emission': forms.HiddenInput(),
334 def __init__(self, *args, **kwargs):
335 super(AbsenceForm, self).__init__(*args, **kwargs)
336 emission = kwargs.get('initial', {}).get('emission')
337 user = kwargs.get('initial', {}).get('user')
338 self.fields['datetime'].widget.emission = emission
339 if user and user.is_staff:
340 date = emission.get_next_planned_date()
341 self.fields['datetime'].initial = date
342 self.fields['datetime'].widget = DateTimeWidget()
344 self.fields['datetime'].widget = AbsenceDateTimeWidget(emission)
346 def save(self, commit=True):
347 t = super(AbsenceForm, self).save(commit=commit)
348 for i, schedule in enumerate(
349 Schedule.objects.filter(emission=self.instance.emission,
350 rerun=True).order_by('datetime')):
351 rerun_date = schedule.get_next_planned_date(self.instance.datetime)
352 rerun_absence = Absence()
353 rerun_absence.emission = self.instance.emission
354 rerun_absence.datetime = rerun_date
359 class PlaylistElementForm(forms.ModelForm):
361 model = PlaylistElement
362 exclude = ('title', 'notes', 'order')
364 'episode': forms.HiddenInput(),
365 'sound': JqueryFileUploadInput(),