forms.py 12.8 KB
Newer Older
1 2 3
import datetime
import re
import unicodedata
fred's avatar
fred committed
4 5
import os
import uuid
6 7 8

from django import forms
from django.forms import fields
9
from django.forms import ValidationError
10

fred's avatar
fred committed
11 12 13
from django.core.files.storage import DefaultStorage
from django.core.urlresolvers import reverse
from django.utils.safestring import mark_safe
fred's avatar
fred committed
14
from django.utils.translation import ugettext_lazy as _
fred's avatar
fred committed
15
from django.utils.encoding import force_text
fred's avatar
fred committed
16 17 18
from django.conf import settings
from django.template.loader import render_to_string

fred's avatar
fred committed
19
from taggit.forms import TagWidget
fred's avatar
fred committed
20

21 22
from .models import (Emission, Episode, Diffusion, Schedule, SoundFile,
        NewsItem, Absence, PlaylistElement)
23
from .utils import get_duration
fred's avatar
fred committed
24
from .widgets import DateTimeWidget, DateWidget
fred's avatar
fred committed
25

26

27 28
def slugify(s):
    s = unicodedata.normalize('NFKD', s).encode('ascii', 'ignore').lower()
fred's avatar
fred committed
29
    return re.sub(r'\W+', '-', force_text(s))
30 31 32 33


class DayAndHourWidget(forms.MultiWidget):
    def __init__(self, attrs=None):
fred's avatar
fred committed
34 35
        WEEKDAYS = [_('Monday'), _('Tuesday'), _('Wednesday'), _('Thursday'),
                _('Friday'), _('Saturday'), _('Sunday')]
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
        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


fred's avatar
fred committed
61
class JqueryFileUploadFileInput(forms.FileInput):
fred's avatar
fred committed
62 63 64 65 66 67 68 69 70 71 72
    template_name = 'emissions/upload.html'

    def get_context(self, name, value, attrs):
        context = super(JqueryFileUploadFileInput, self).get_context(name, value, attrs)
        context['widget'].update({
            'upload_url': self.url,
            'files': self.files,
            'STATIC_URL': settings.STATIC_URL}
        )
        return context

fred's avatar
fred committed
73
    def render(self, name, value, attrs=None):
fred's avatar
fred committed
74
        output = render_to_string('emissions/upload.html', {'widget': {
fred's avatar
fred committed
75 76 77
            'upload_url': self.url,
            'files': self.files,
            'name': name,
fred's avatar
fred committed
78
            'STATIC_URL': settings.STATIC_URL}})
fred's avatar
fred committed
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
        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 = []
fred's avatar
fred committed
125 126 127 128
        try:
            return file_input[0]
        except IndexError:
            return None
fred's avatar
fred committed
129 130 131 132 133 134 135 136

    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)
fred's avatar
fred committed
137
        output = super(JqueryFileUploadInput, self).render(name, value, attrs)
fred's avatar
fred committed
138 139 140 141
        fileinput_id = '%s_%s' % (attrs['id'], '1')
        return output


142 143 144
class EmissionForm(forms.ModelForm):
    class Meta:
        model = Emission
145
        exclude = ('slug', 'colours', 'has_focus', 'got_focus', 'chat_open', 'podcast_sound_quality')
146 147 148 149 150 151 152 153 154 155

    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
156
        exclude = ('slug', 'has_focus', 'got_focus', 'effective_start', 'effective_end')
fred's avatar
fred committed
157 158
        widgets = {'emission': forms.HiddenInput(),
                   'tags': TagWidget()}
159

fred's avatar
fred committed
160 161
    def save(self, commit=True):
        if not self.instance.slug:
162 163 164 165 166 167 168 169 170 171 172 173
            base_slug = slugify(self.instance.title)
            slug = base_slug
            i = 1
            while True:
                try:
                    Episode.objects.get(slug=slug)
                except Episode.DoesNotExist:
                    break
                i += 1
                slug = '%s-%s' % (base_slug, i)

            self.instance.slug = slug
fred's avatar
fred committed
174 175
        return super(EpisodeForm, self).save(commit=commit)

176 177

class EpisodeNewForm(EpisodeForm):
178
    first_diffusion = forms.DateTimeField(label=_('First Diffusion'),
179
            widget=DateTimeWidget)
180 181 182 183 184 185 186 187 188
    second_diffusion = forms.DateTimeField(label=_('Second Diffusion'),
            widget=DateTimeWidget)
    third_diffusion = forms.DateTimeField(label=_('Third Diffusion'),
            widget=DateTimeWidget)
    fourth_diffusion = forms.DateTimeField(label=_('Fourth Diffusion'),
            widget=DateTimeWidget)

    def get_diffusion_fields(self, emission):
        if emission:
189
            schedules = list(Schedule.objects.filter(emission=emission).order_by('datetime'))
190 191 192
            if len(schedules) > 1 and (
                schedules[0].weeks == schedules[1].weeks and
                schedules[0].datetime.date() == schedules[1].datetime.date()):
193 194 195 196
                # special case for daily program with same-day rerun
                schedules = 2
            else:
                schedules = len([x for x in schedules if x.rerun]) + 1
197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
        else:
            schedules = 1
        fields = ['first_diffusion']
        if schedules > 1:
            fields.append('second_diffusion')
        if schedules > 2:
            fields.append('third_diffusion')
        if schedules > 3:
            fields.append('fourth_diffusion')
        return fields

    def __init__(self, *args, **kwargs):
        super(EpisodeNewForm, self).__init__(*args, **kwargs)
        emission = kwargs.get('initial', {}).get('emission')
        diffusion_fields = self.get_diffusion_fields(emission)
212
        for field_name in list(self.fields.keys()):
213 214
            if field_name.endswith('_diffusion') and not field_name in diffusion_fields:
                del self.fields[field_name]
215 216 217

    def save(self, commit=True):
        episode = super(EpisodeNewForm, self).save(commit=commit)
218 219 220 221 222
        for field_name in self.get_diffusion_fields(episode.emission):
            diffusion = Diffusion()
            diffusion.episode_id = episode.id
            diffusion.datetime = self.cleaned_data.get(field_name)
            diffusion.save()
223
        return episode
fred's avatar
fred committed
224 225 226 227 228


class ScheduleForm(forms.ModelForm):
    class Meta:
        model = Schedule
fred's avatar
fred committed
229
        exclude = ()
fred's avatar
fred committed
230 231 232 233
        widgets = {
                'emission': forms.HiddenInput(),
                'datetime': DayAndHourWidget(),
        }
fred's avatar
fred committed
234 235 236 237 238


class SoundFileForm(forms.ModelForm):
    class Meta:
        model = SoundFile
239
        exclude = ('has_focus', 'got_focus', 'duration')
fred's avatar
fred committed
240 241 242 243
        widgets = {
                'episode': forms.HiddenInput(),
                'file': JqueryFileUploadInput(),
        }
244

245 246 247 248 249 250 251
    def clean(self):
        super().clean()
        if self.cleaned_data.get('file'):
            duration = get_duration(self.cleaned_data['file'].file.name)
            if not duration:
                raise ValidationError(_('Invalid file, could not get duration.'))

252

fred's avatar
fred committed
253 254 255
class SoundFileEditForm(forms.ModelForm):
    class Meta:
        model = SoundFile
256
        exclude = ('has_focus', 'got_focus', 'file', 'duration')
fred's avatar
fred committed
257 258 259 260 261 262
        widgets = {
                'episode': forms.HiddenInput(),
        }

    def __init__(self, *args, **kwargs):
        super(SoundFileEditForm, self).__init__(*args, **kwargs)
263
        self.fields.keyOrder = ['title', 'format', 'podcastable', 'fragment', 'license']
fred's avatar
fred committed
264 265


266 267 268
class DiffusionForm(forms.ModelForm):
    class Meta:
        model = Diffusion
fred's avatar
fred committed
269
        exclude = ()
270 271
        widgets = {
                'episode': forms.HiddenInput(),
272
                'datetime': DateTimeWidget(),
273
        }
fred's avatar
fred committed
274 275 276 277 278


class NewsItemForm(forms.ModelForm):
    class Meta:
        model = NewsItem
fred's avatar
fred committed
279
        exclude = ('slug', 'has_focus', 'got_focus')
fred's avatar
fred committed
280 281 282
        widgets = {'tags': TagWidget(),
                   'date': DateWidget(),
                   'expiration_date': DateWidget(),
fred's avatar
fred committed
283
                   'event_date': DateWidget(),
fred's avatar
fred committed
284
                  }
fred's avatar
fred committed
285

286 287 288 289 290 291
    def __init__(self, *args, **kwargs):
        super(NewsItemForm, self).__init__(*args, **kwargs)
        user = kwargs.get('initial', {}).get('user')
        if not user.is_staff:
            self.fields['emission'].widget = forms.HiddenInput()

fred's avatar
fred committed
292 293
    def save(self, commit=True):
        if not self.instance.slug:
294 295 296 297 298 299 300 301 302 303 304 305
            base_slug = slugify(self.instance.title)
            slug = base_slug
            i = 1
            while True:
                try:
                    NewsItem.objects.get(slug=slug)
                except NewsItem.DoesNotExist:
                    break
                i += 1
                slug = '%s-%s' % (base_slug, i)

            self.instance.slug = slug
fred's avatar
fred committed
306
        return super(NewsItemForm, self).save(commit=commit)
fred's avatar
fred committed
307 308 309


class AbsenceDateTimeWidget(forms.Select):
310 311 312 313 314
    def __init__(self, emission, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.choices = list(self.options(emission))

    def options(self, emission):
fred's avatar
fred committed
315 316 317 318 319 320
        dates = []
        for i in range(4):
            if dates:
                since = dates[-1] + datetime.timedelta(minutes=10)
            else:
                since = None
321 322 323
            dates.append(emission.get_next_planned_date(since))
        for date in dates:
            yield (date, date.strftime('%d/%m/%Y %H:%M'))
fred's avatar
fred committed
324 325 326 327 328


class AbsenceForm(forms.ModelForm):
    class Meta:
        model = Absence
fred's avatar
fred committed
329
        exclude = ()
fred's avatar
fred committed
330 331 332 333 334 335 336 337 338 339 340 341 342
        widgets = {
                'emission': forms.HiddenInput(),
        }

    def __init__(self, *args, **kwargs):
        super(AbsenceForm, self).__init__(*args, **kwargs)
        emission = kwargs.get('initial', {}).get('emission')
        user = kwargs.get('initial', {}).get('user')
        self.fields['datetime'].widget.emission = emission
        if user and user.is_staff:
            date = emission.get_next_planned_date()
            self.fields['datetime'].initial = date
            self.fields['datetime'].widget = DateTimeWidget()
343 344
        else:
            self.fields['datetime'].widget = AbsenceDateTimeWidget(emission)
fred's avatar
fred committed
345 346 347 348 349 350 351 352 353 354 355 356

    def save(self, commit=True):
        t = super(AbsenceForm, self).save(commit=commit)
        for i, schedule in enumerate(
                Schedule.objects.filter(emission=self.instance.emission,
                    rerun=True).order_by('datetime')):
            rerun_date = schedule.get_next_planned_date(self.instance.datetime)
            rerun_absence = Absence()
            rerun_absence.emission = self.instance.emission
            rerun_absence.datetime = rerun_date
            rerun_absence.save()
        return t
357 358 359 360 361 362 363 364 365 366


class PlaylistElementForm(forms.ModelForm):
    class Meta:
        model = PlaylistElement
        exclude = ('title', 'notes', 'order')
        widgets = {
                'episode': forms.HiddenInput(),
                'sound': JqueryFileUploadInput(),
        }