]> git.0d.be Git - django-panik-emissions.git/blob - emissions/forms.py
047e332482f994854750e5da63479e3d957c9dc4
[django-panik-emissions.git] / emissions / forms.py
1 import datetime
2 import re
3 import unicodedata
4 import os
5 import uuid
6
7 from django import forms
8 from django.forms import fields
9 from django.forms import ValidationError
10
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
18
19 from taggit.forms import TagWidget
20
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
25
26
27 def slugify(s):
28     s = unicodedata.normalize('NFKD', s).encode('ascii', 'ignore').lower()
29     return re.sub(r'\W+', '-', force_text(s))
30
31
32 class DayAndHourWidget(forms.MultiWidget):
33     def __init__(self, attrs=None):
34         WEEKDAYS = [_('Monday'), _('Tuesday'), _('Wednesday'), _('Thursday'),
35                 _('Friday'), _('Saturday'), _('Sunday')]
36         widgets = (
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)])),
40         )
41         super(DayAndHourWidget, self).__init__(widgets, attrs)
42
43     def decompress(self, value):
44         if value:
45             return [value.weekday(), value.hour, value.minute]
46         return [None, None, None]
47
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.
52         data_list = [
53             widget.value_from_datadict(data, files, name + '_%s' % i)
54             for i, widget in enumerate(self.widgets)]
55
56         if data_list:
57             return datetime.datetime(2007, 1, int(data_list[0])+1, int(data_list[1]), int(data_list[2]))
58         return None
59
60
61 class JqueryFileUploadFileInput(forms.FileInput):
62     template_name = 'emissions/upload.html'
63
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,
68             'files': self.files,
69             'STATIC_URL': settings.STATIC_URL}
70         )
71         return context
72
73     def render(self, name, value, attrs=None):
74         output = render_to_string('emissions/upload.html', {'widget': {
75             'upload_url': self.url,
76             'files': self.files,
77             'name': name,
78             'STATIC_URL': settings.STATIC_URL}})
79         return mark_safe(output)
80
81
82 class JqueryFileUploadInput(forms.MultiWidget):
83     needs_multipart_form = True
84     upload_id_re = re.compile(r'^[a-z0-9A-Z-]+$')
85     upload_id = None
86
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)
92
93     def decompress(self, value):
94         # map python value to widget contents
95         if self.upload_id:
96             pass
97         elif isinstance(value, (list, tuple)) and value and value[0] is not None:
98             self.upload_id = str(value[0])
99         else:
100             self.upload_id = str(uuid.uuid4())
101         return [self.upload_id, None]
102
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):
107             return
108         for filepath in storage.listdir(path)[1]:
109             name = os.path.basename(filepath)
110             yield storage.open(os.path.join(path, name))
111
112     def value_from_datadict(self, data, files, name):
113         '''
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
117         '''
118         upload_id, file_input = super(JqueryFileUploadInput, self).value_from_datadict(data, files, name)
119         if file_input:
120             pass
121         elif JqueryFileUploadInput.upload_id_re.match(upload_id):
122             file_input = list(self.get_files_for_id(upload_id))
123         else:
124             file_input = []
125         try:
126             return file_input[0]
127         except IndexError:
128             return None
129
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')
139         return output
140
141
142 class EmissionForm(forms.ModelForm):
143     class Meta:
144         model = Emission
145         exclude = ('slug', 'colours', 'has_focus', 'got_focus', 'chat_open', 'podcast_sound_quality')
146
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)
151
152
153 class EpisodeForm(forms.ModelForm):
154     class Meta:
155         model = Episode
156         exclude = ('slug', 'has_focus', 'got_focus', 'effective_start', 'effective_end')
157         widgets = {'emission': forms.HiddenInput(),
158                    'tags': TagWidget()}
159
160     def save(self, commit=True):
161         if not self.instance.slug:
162             base_slug = slugify(self.instance.title)
163             slug = base_slug
164             i = 1
165             while True:
166                 try:
167                     Episode.objects.get(slug=slug)
168                 except Episode.DoesNotExist:
169                     break
170                 i += 1
171                 slug = '%s-%s' % (base_slug, i)
172
173             self.instance.slug = slug
174         return super(EpisodeForm, self).save(commit=commit)
175
176
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)
186
187     def get_diffusion_fields(self, emission):
188         if 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
194                 schedules = 2
195             else:
196                 schedules = len([x for x in schedules if x.rerun]) + 1
197         else:
198             schedules = 1
199         fields = ['first_diffusion']
200         if schedules > 1:
201             fields.append('second_diffusion')
202         if schedules > 2:
203             fields.append('third_diffusion')
204         if schedules > 3:
205             fields.append('fourth_diffusion')
206         return fields
207
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]
215
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)
222             diffusion.save()
223         return episode
224
225
226 class ScheduleForm(forms.ModelForm):
227     class Meta:
228         model = Schedule
229         exclude = ()
230         widgets = {
231                 'emission': forms.HiddenInput(),
232                 'datetime': DayAndHourWidget(),
233         }
234
235
236 class SoundFileForm(forms.ModelForm):
237     class Meta:
238         model = SoundFile
239         exclude = ('has_focus', 'got_focus', 'duration')
240         widgets = {
241                 'episode': forms.HiddenInput(),
242                 'file': JqueryFileUploadInput(),
243         }
244
245     def clean(self):
246         super().clean()
247         if self.cleaned_data.get('file'):
248             duration = get_duration(self.cleaned_data['file'].file.name)
249             if not duration:
250                 raise ValidationError(_('Invalid file, could not get duration.'))
251
252
253 class SoundFileEditForm(forms.ModelForm):
254     class Meta:
255         model = SoundFile
256         exclude = ('has_focus', 'got_focus', 'file', 'duration')
257         widgets = {
258                 'episode': forms.HiddenInput(),
259         }
260
261     def __init__(self, *args, **kwargs):
262         super(SoundFileEditForm, self).__init__(*args, **kwargs)
263         self.fields.keyOrder = ['title', 'format', 'podcastable', 'fragment', 'license']
264
265
266 class DiffusionForm(forms.ModelForm):
267     class Meta:
268         model = Diffusion
269         exclude = ()
270         widgets = {
271                 'episode': forms.HiddenInput(),
272                 'datetime': DateTimeWidget(),
273         }
274
275
276 class NewsItemForm(forms.ModelForm):
277     class Meta:
278         model = NewsItem
279         exclude = ('slug', 'has_focus', 'got_focus')
280         widgets = {'tags': TagWidget(),
281                    'date': DateWidget(),
282                    'expiration_date': DateWidget(),
283                    'event_date': DateWidget(),
284                   }
285
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()
291
292     def save(self, commit=True):
293         if not self.instance.slug:
294             base_slug = slugify(self.instance.title)
295             slug = base_slug
296             i = 1
297             while True:
298                 try:
299                     NewsItem.objects.get(slug=slug)
300                 except NewsItem.DoesNotExist:
301                     break
302                 i += 1
303                 slug = '%s-%s' % (base_slug, i)
304
305             self.instance.slug = slug
306         return super(NewsItemForm, self).save(commit=commit)
307
308
309 class AbsenceDateTimeWidget(forms.Select):
310     def __init__(self, emission, *args, **kwargs):
311         super().__init__(*args, **kwargs)
312         self.choices = list(self.options(emission))
313
314     def options(self, emission):
315         dates = []
316         for i in range(4):
317             if dates:
318                 since = dates[-1] + datetime.timedelta(minutes=10)
319             else:
320                 since = None
321             dates.append(emission.get_next_planned_date(since))
322         for date in dates:
323             yield (date, date.strftime('%d/%m/%Y %H:%M'))
324
325
326 class AbsenceForm(forms.ModelForm):
327     class Meta:
328         model = Absence
329         exclude = ()
330         widgets = {
331                 'emission': forms.HiddenInput(),
332         }
333
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()
343         else:
344             self.fields['datetime'].widget = AbsenceDateTimeWidget(emission)
345
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
355             rerun_absence.save()
356         return t
357
358
359 class PlaylistElementForm(forms.ModelForm):
360     class Meta:
361         model = PlaylistElement
362         exclude = ('title', 'notes', 'order')
363         widgets = {
364                 'episode': forms.HiddenInput(),
365                 'sound': JqueryFileUploadInput(),
366         }