]> git.0d.be Git - django-panik-nonstop.git/blob - nonstop/views.py
add possibility to push diffusion to soma (draft)
[django-panik-nonstop.git] / nonstop / views.py
1 import copy
2 import csv
3 import datetime
4 from cStringIO import StringIO
5 import os
6 import tempfile
7
8 import mutagen
9
10 from django.core.files.storage import default_storage
11 from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
12 from django.core.urlresolvers import reverse
13 from django.contrib import messages
14 from django.db.models import Q
15 from django.http import HttpResponse, HttpResponseRedirect
16 from django.utils.translation import ugettext_lazy as _
17 from django.views.generic.base import RedirectView, TemplateView
18 from django.views.generic.dates import DayArchiveView
19 from django.views.generic.detail import DetailView
20 from django.views.generic.edit import FormView
21 from django.views.generic.list import ListView
22
23 from .forms import UploadTracksForm, TrackMetaForm, TrackSearchForm, CleanupForm
24 from .models import SomaLogLine, Track, Artist, NonstopFile
25 from emissions.models import Nonstop, Diffusion
26
27 from . import utils
28
29
30 class SomaDayArchiveView(DayArchiveView):
31     queryset = SomaLogLine.objects.all()
32     date_field = "play_timestamp"
33     make_object_list = True
34     allow_future = False
35     month_format = '%m'
36
37
38 class SomaDayArchiveCsvView(SomaDayArchiveView):
39     def render_to_response(self, context, **response_kwargs):
40         out = StringIO()
41         writer = csv.writer(out)
42         for line in context['object_list']:
43             if line.filepath.track:
44                 writer.writerow([line.play_timestamp.strftime('%Y-%m-%d %H:%M'),
45                     line.filepath.short.encode('utf-8', 'replace'),
46                     line.filepath.track.title.encode('utf-8', 'replace'),
47                     line.filepath.track.artist.name.encode('utf-8', 'replace'),
48                     line.filepath.track.language,
49                     line.filepath.track.instru and 'instru' or '',
50                     line.filepath.track.cfwb and 'cfwb' or '',
51                     line.filepath.track.added_to_nonstop_timestamp.strftime('%Y-%m-%d %H:%M') if line.filepath.added_to_nonstop_timestamp else '',
52                     ])
53             else:
54                 writer.writerow([line.play_timestamp.strftime('%Y-%m-%d %H:%M'),
55                                 line.filepath.short.encode('utf-8', 'replace')])
56         return HttpResponse(out.getvalue(), content_type='text/csv; charset=utf-8')
57
58
59 class RedirectTodayView(RedirectView):
60     def get_redirect_url(self, *args, **kwargs):
61         today = datetime.datetime.today()
62         return reverse('archive_day', kwargs={
63                         'year': today.year,
64                         'month': today.month,
65                         'day': today.day})
66
67
68 class TrackDetailView(DetailView):
69     model = Track
70
71     def get_context_data(self, **kwargs):
72         ctx = super(TrackDetailView, self).get_context_data(**kwargs)
73         ctx['metadata_form'] = TrackMetaForm(instance=self.object)
74         return ctx
75
76     def post(self, request, *args, **kwargs):
77         assert self.request.user.has_perm('nonstop.add_track')
78         instance = self.get_object()
79         old_nonstop_zones = copy.copy(instance.nonstop_zones.all())
80         form = TrackMetaForm(request.POST, instance=instance)
81         form.save()
82         new_nonstop_zones = self.get_object().nonstop_zones.all()
83         if set(old_nonstop_zones) != set(new_nonstop_zones):
84             instance.sync_nonstop_zones()
85         return HttpResponseRedirect('.')
86
87
88 class ArtistDetailView(DetailView):
89     model = Artist
90
91
92 class ArtistListView(ListView):
93     model = Artist
94
95
96 class ZoneStats(object):
97     def __init__(self, zone, from_date=None, until_date=None, **kwargs):
98         self.zone = zone
99         self.qs = Track.objects.filter(nonstop_zones=self.zone, **kwargs)
100         self.from_date = from_date
101         if from_date:
102             self.qs = self.qs.filter(nonstopfile__somalogline__play_timestamp__gte=from_date)
103         if until_date:
104             self.qs = self.qs.filter(nonstopfile__somalogline__play_timestamp__lte=until_date)
105         self.qs = self.qs.distinct()
106
107     def count(self, **kwargs):
108         return self.qs.filter(**kwargs).count()
109
110     def percentage(self, **kwargs):
111         total = self.count()
112         if total == 0:
113             return '-'
114         return '%.2f%%' % (100. * self.count(**kwargs) / total)
115
116     def instru(self):
117         return self.count(instru=True)
118
119     def instru_percentage(self):
120         return self.percentage(instru=True)
121
122     def sabam(self):
123         return self.count(sabam=True)
124
125     def sabam_percentage(self):
126         return self.percentage(sabam=True)
127
128     def cfwb(self):
129         return self.count(cfwb=True)
130
131     def cfwb_percentage(self):
132         return self.percentage(cfwb=True)
133
134     def french(self):
135         return self.count(language='fr')
136
137     def french_percentage(self):
138         considered_tracks = self.count() - self.instru()
139         if considered_tracks == 0:
140             return '-'
141         return '%.2f%%' % (100. * self.french() / considered_tracks)
142
143     def new_files(self):
144         return self.count(nonstopfile__creation_timestamp__gte=self.from_date)
145
146     def percent_new_files(self):
147         return self.percentage(nonstopfile__creation_timestamp__gte=self.from_date)
148
149
150 def parse_date(date):
151     if date.endswith('d'):
152         return datetime.datetime.today() + datetime.timedelta(int(date.rstrip('d')))
153     return datetime.datetime.strptime(date, '%Y-%m-%d').date()
154
155 class StatisticsView(TemplateView):
156     template_name = 'nonstop/statistics.html'
157
158     def get_context_data(self, **kwargs):
159         context = super(StatisticsView, self).get_context_data(**kwargs)
160         context['zones'] = Nonstop.objects.all().order_by('start')
161         kwargs = {}
162         if 'from' in self.request.GET:
163             kwargs['from_date'] = parse_date(self.request.GET['from'])
164             context['from_date'] = kwargs['from_date']
165         if 'until' in self.request.GET:
166             kwargs['until_date'] = parse_date(self.request.GET['until'])
167         if 'onair' in self.request.GET:
168             kwargs['nonstopfile__somalogline__on_air'] = True
169         for zone in context['zones']:
170             zone.stats = ZoneStats(zone, **kwargs)
171         return context
172
173
174 class UploadTracksView(FormView):
175     form_class = UploadTracksForm
176     template_name = 'nonstop/upload.html'
177     success_url = '.'
178
179     def post(self, request, *args, **kwargs):
180         assert self.request.user.has_perm('nonstop.add_track')
181         form_class = self.get_form_class()
182         form = self.get_form(form_class)
183         tracks = request.FILES.getlist('tracks')
184         if not form.is_valid():
185             return self.form_invalid(form)
186         missing_metadata = []
187         metadatas = {}
188         for f in tracks:
189             with tempfile.NamedTemporaryFile(prefix='track-upload') as tmpfile:
190                 tmpfile.write(f.read())
191                 f.seek(0)
192                 metadata = mutagen.File(tmpfile.name, easy=True)
193             if not metadata or not metadata.get('artist') or not metadata.get('title'):
194                 missing_metadata.append(f.name)
195             else:
196                 metadatas[f.name] = metadata
197         if missing_metadata:
198             form.add_error('tracks', _('Missing metadata in: ') + ', '.join(missing_metadata))
199             return self.form_invalid(form)
200
201         for f in tracks:
202             metadata = metadatas[f.name]
203             artist_name = metadata.get('artist')[0]
204             track_title = metadata.get('title')[0]
205
206             monthdir = datetime.datetime.today().strftime('%Y-%m')
207             filepath = '%s/%s - %s - %s%s' % (monthdir,
208                 datetime.datetime.today().strftime('%y%m%d'),
209                 artist_name[:50].replace('/', ' ').strip(),
210                 track_title[:80].replace('/', ' ').strip(),
211                 os.path.splitext(f.name)[-1])
212
213             default_storage.save(os.path.join('nonstop', 'tracks', filepath), content=f)
214
215             nonstop_file = NonstopFile()
216             nonstop_file.set_track_filepath(filepath)
217             artist, created = Artist.objects.get_or_create(name=artist_name)
218             track, created = Track.objects.get_or_create(title=track_title, artist=artist,
219                     defaults={'uploader': self.request.user})
220             nonstop_file.track = track
221             nonstop_file.save()
222             if request.POST.get('nonstop_zone'):
223                 track.nonstop_zones.add(
224                         Nonstop.objects.get(id=request.POST.get('nonstop_zone')))
225             nonstop_file.track.sync_nonstop_zones()
226
227         messages.info(self.request, '%d new track(s)' % len(tracks))
228         return self.form_valid(form)
229
230
231 class RecentTracksView(ListView):
232     template_name = 'nonstop/recent_tracks.html'
233
234     def get_queryset(self):
235         return Track.objects.exclude(creation_timestamp__isnull=True).order_by('-creation_timestamp')[:50]
236
237     def post(self, request, *args, **kwargs):
238         assert self.request.user.has_perm('nonstop.add_track')
239         for track_id in request.POST.getlist('track'):
240             track = Track.objects.get(id=track_id)
241             track.language = request.POST.get('lang-%s' % track_id, '')
242             track.instru = 'instru-%s' % track_id in request.POST
243             track.sabam = 'sabam-%s' % track_id in request.POST
244             track.cfwb = 'cfwb-%s' % track_id in request.POST
245             track.save()
246         return HttpResponseRedirect('.')
247
248
249 class QuickLinksView(TemplateView):
250     template_name = 'nonstop/quick_links.html'
251
252
253 class SearchView(TemplateView):
254     template_name = 'nonstop/search.html'
255
256     def get_queryset(self):
257         queryset = Track.objects.all()
258
259         q = self.request.GET.get('q')
260         if q:
261             queryset = queryset.filter(Q(title__icontains=q.lower()) | Q(artist__name__icontains=q.lower()))
262
263         zone = self.request.GET.get('zone')
264         if zone:
265             from emissions.models import Nonstop
266             if zone == 'none':
267                 queryset = queryset.filter(nonstop_zones=None)
268             elif zone == 'any':
269                 queryset = queryset.filter(nonstop_zones__isnull=False).distinct()
270             else:
271                 queryset = queryset.filter(nonstop_zones=zone)
272
273         order = self.request.GET.get('order_by') or 'title'
274         if order:
275             if 'added_to_nonstop_timestamp' in order:
276                 queryset = queryset.filter(added_to_nonstop_timestamp__isnull=False)
277             queryset = queryset.order_by(order)
278         return queryset
279
280     def get_context_data(self, **kwargs):
281         ctx = super(SearchView, self).get_context_data(**kwargs)
282         ctx['form'] = TrackSearchForm(self.request.GET)
283         queryset = self.get_queryset()
284         qs = self.request.GET.copy()
285         qs.pop('page', None)
286         ctx['qs'] = qs.urlencode()
287
288         tracks = Paginator(queryset.select_related(), 20)
289
290         page = self.request.GET.get('page')
291         try:
292             ctx['tracks'] = tracks.page(page)
293         except PageNotAnInteger:
294             ctx['tracks'] = tracks.page(1)
295         except EmptyPage:
296             ctx['tracks'] = tracks.page(tracks.num_pages)
297
298         return ctx
299
300
301 class SearchCsvView(SearchView):
302     def get(self, request, *args, **kwargs):
303         out = StringIO()
304         writer = csv.writer(out)
305         writer.writerow(['Title', 'Artist', 'Zones', 'Language', 'Instru', 'CFWB', 'Ajout'])
306         for track in self.get_queryset():
307             writer.writerow([
308                 track.title.encode('utf-8', 'replace') if track.title else 'Inconnu',
309                 track.artist.name.encode('utf-8', 'replace') if (track.artist and track.artist.name) else 'Inconnu',
310                 ' + '.join([x.title.encode('utf-8') for x in track.nonstop_zones.all()]),
311                 track.language or '',
312                 track.instru and 'instru' or '',
313                 track.cfwb and 'cfwb' or '',
314                 track.added_to_nonstop_timestamp.strftime('%Y-%m-%d %H:%M') if track.added_to_nonstop_timestamp else '',
315                 ])
316         return HttpResponse(out.getvalue(), content_type='text/csv; charset=utf-8')
317
318
319 class CleanupView(TemplateView):
320     template_name = 'nonstop/cleanup.html'
321
322     def get_context_data(self, **kwargs):
323         ctx = super(CleanupView, self).get_context_data(**kwargs)
324         ctx['form'] = CleanupForm()
325
326         zone = self.request.GET.get('zone')
327         if zone:
328             from emissions.models import Nonstop
329             ctx['zone'] = Nonstop.objects.get(id=zone)
330             ctx['count'] = Track.objects.filter(nonstop_zones=zone).count()
331             ctx['tracks'] = Track.objects.filter(nonstop_zones=zone).order_by(
332                             'added_to_nonstop_timestamp').select_related()[:30]
333         return ctx
334
335     def post(self, request, *args, **kwargs):
336         assert self.request.user.has_perm('nonstop.add_track')
337         count = 0
338         for track_id in request.POST.getlist('track'):
339             if request.POST.get('remove-%s' % track_id):
340                 track = Track.objects.get(id=track_id)
341                 track.nonstop_zones.clear()
342                 track.sync_nonstop_zones()
343                 count += 1
344         if count:
345             messages.info(self.request, 'Removed %d new track(s)' % count)
346         return HttpResponseRedirect('.')
347
348
349 class AddDiffusionView(DetailView):
350     model = Diffusion
351
352     def get(self, request, *args, **kwargs):
353         diffusion = self.get_object()
354         utils.add_diffusion(diffusion)
355         episode = diffusion.episode
356         messages.info(self.request, _('%s added to soma') % episode.emission.title)
357         return HttpResponseRedirect(reverse('episode-view', kwargs={
358             'emission_slug': episode.emission.slug,
359             'slug': episode.slug}))