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