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