4 from cStringIO import StringIO
10 from django.core.files.storage import default_storage
11 from django.core.urlresolvers import reverse
12 from django.contrib import messages
13 from django.db.models import Q
14 from django.http import HttpResponse, HttpResponseRedirect
15 from django.utils.translation import ugettext_lazy as _
16 from django.views.generic.base import RedirectView, TemplateView
17 from django.views.generic.dates import DayArchiveView
18 from django.views.generic.detail import DetailView
19 from django.views.generic.edit import FormView
20 from django.views.generic.list import ListView
22 from .forms import UploadTracksForm, TrackMetaForm, TrackSearchForm, CleanupForm
23 from .models import SomaLogLine, Track, Artist, NonstopFile
24 from emissions.models import Nonstop
26 class SomaDayArchiveView(DayArchiveView):
27 queryset = SomaLogLine.objects.all()
28 date_field = "play_timestamp"
29 make_object_list = True
34 class SomaDayArchiveCsvView(SomaDayArchiveView):
35 def render_to_response(self, context, **response_kwargs):
37 writer = csv.writer(out)
38 for line in context['object_list']:
39 if line.filepath.track:
40 writer.writerow([line.play_timestamp.strftime('%Y-%m-%d %H:%M'),
41 line.filepath.short.encode('utf-8', 'replace'),
42 line.filepath.track.title.encode('utf-8', 'replace'),
43 line.filepath.track.artist.name.encode('utf-8', 'replace'),
44 line.filepath.track.language,
45 line.filepath.track.instru and 'instru' or '',
46 line.filepath.track.cfwb and 'cfwb' or ''])
48 writer.writerow([line.play_timestamp.strftime('%Y-%m-%d %H:%M'),
49 line.filepath.short.encode('utf-8', 'replace')])
50 return HttpResponse(out.getvalue(), content_type='text/csv; charset=utf-8')
53 class RedirectTodayView(RedirectView):
54 def get_redirect_url(self, *args, **kwargs):
55 today = datetime.datetime.today()
56 return reverse('archive_day', kwargs={
62 class TrackDetailView(DetailView):
65 def get_context_data(self, **kwargs):
66 ctx = super(TrackDetailView, self).get_context_data(**kwargs)
67 ctx['metadata_form'] = TrackMetaForm(instance=self.object)
70 def post(self, request, *args, **kwargs):
71 instance = self.get_object()
72 old_nonstop_zones = copy.copy(instance.nonstop_zones.all())
73 form = TrackMetaForm(request.POST, instance=instance)
75 new_nonstop_zones = self.get_object().nonstop_zones.all()
76 if set(old_nonstop_zones) != set(new_nonstop_zones):
77 instance.sync_nonstop_zones()
78 return HttpResponseRedirect('.')
81 class ArtistDetailView(DetailView):
85 class ArtistListView(ListView):
89 class ZoneStats(object):
90 def __init__(self, zone, from_date=None, until_date=None, **kwargs):
92 self.qs = Track.objects.filter(nonstop_zones=self.zone, **kwargs)
93 self.from_date = from_date
95 self.qs = self.qs.filter(nonstopfile__somalogline__play_timestamp__gte=from_date)
97 self.qs = self.qs.filter(nonstopfile__somalogline__play_timestamp__lte=until_date)
98 self.qs = self.qs.distinct()
100 def count(self, **kwargs):
101 return self.qs.filter(**kwargs).count()
103 def percentage(self, **kwargs):
107 return '%.2f%%' % (100. * self.count(**kwargs) / total)
110 return self.count(instru=True)
112 def instru_percentage(self):
113 return self.percentage(instru=True)
116 return self.count(sabam=True)
118 def sabam_percentage(self):
119 return self.percentage(sabam=True)
122 return self.count(cfwb=True)
124 def cfwb_percentage(self):
125 return self.percentage(cfwb=True)
128 return self.count(language='fr')
130 def french_percentage(self):
131 considered_tracks = self.count() - self.instru()
132 if considered_tracks == 0:
134 return '%.2f%%' % (100. * self.french() / considered_tracks)
137 return self.count(nonstopfile__creation_timestamp__gte=self.from_date)
139 def percent_new_files(self):
140 return self.percentage(nonstopfile__creation_timestamp__gte=self.from_date)
143 def parse_date(date):
144 if date.endswith('d'):
145 return datetime.datetime.today() + datetime.timedelta(int(date.rstrip('d')))
146 return datetime.datetime.strptime(date, '%Y-%m-%d').date()
148 class StatisticsView(TemplateView):
149 template_name = 'nonstop/statistics.html'
151 def get_context_data(self, **kwargs):
152 context = super(StatisticsView, self).get_context_data(**kwargs)
153 context['zones'] = Nonstop.objects.all().order_by('start')
155 if 'from' in self.request.GET:
156 kwargs['from_date'] = parse_date(self.request.GET['from'])
157 context['from_date'] = kwargs['from_date']
158 if 'until' in self.request.GET:
159 kwargs['until_date'] = parse_date(self.request.GET['until'])
160 if 'onair' in self.request.GET:
161 kwargs['nonstopfile__somalogline__on_air'] = True
162 for zone in context['zones']:
163 zone.stats = ZoneStats(zone, **kwargs)
167 class UploadTracksView(FormView):
168 form_class = UploadTracksForm
169 template_name = 'nonstop/upload.html'
172 def post(self, request, *args, **kwargs):
173 form_class = self.get_form_class()
174 form = self.get_form(form_class)
175 tracks = request.FILES.getlist('tracks')
176 if not form.is_valid():
177 return self.form_invalid(form)
178 missing_metadata = []
181 with tempfile.NamedTemporaryFile(prefix='track-upload') as tmpfile:
182 tmpfile.write(f.read())
184 metadata = mutagen.File(tmpfile.name, easy=True)
185 if not metadata or not metadata.get('artist') or not metadata.get('title'):
186 missing_metadata.append(f.name)
188 metadatas[f.name] = metadata
190 form.add_error('tracks', _('Missing metadata in: ') + ', '.join(missing_metadata))
191 return self.form_invalid(form)
194 metadata = metadatas[f.name]
195 artist_name = metadata.get('artist')[0]
196 track_title = metadata.get('title')[0]
198 monthdir = datetime.datetime.today().strftime('%Y-%m')
199 filepath = '%s/%s - %s - %s%s' % (monthdir,
200 datetime.datetime.today().strftime('%y%m%d'),
201 artist_name[:50].replace('/', ' ').strip(),
202 track_title[:80].replace('/', ' ').strip(),
203 os.path.splitext(f.name)[-1])
205 default_storage.save(os.path.join('nonstop', 'tracks', filepath), content=f)
207 nonstop_file = NonstopFile()
208 nonstop_file.set_track_filepath(filepath)
209 artist, created = Artist.objects.get_or_create(name=artist_name)
210 track, created = Track.objects.get_or_create(title=track_title, artist=artist,
211 defaults={'uploader': self.request.user})
212 nonstop_file.track = track
214 if request.POST.get('nonstop_zone'):
215 track.nonstop_zones.add(
216 Nonstop.objects.get(id=request.POST.get('nonstop_zone')))
217 nonstop_file.track.sync_nonstop_zones()
219 messages.info(self.request, '%d new track(s)' % len(tracks))
220 return self.form_valid(form)
223 class RecentTracksView(ListView):
224 template_name = 'nonstop/recent_tracks.html'
226 def get_queryset(self):
227 return Track.objects.exclude(creation_timestamp__isnull=True).order_by('-creation_timestamp')[:50]
229 def post(self, request, *args, **kwargs):
230 for track_id in request.POST.getlist('track'):
231 track = Track.objects.get(id=track_id)
232 track.language = request.POST.get('lang-%s' % track_id, '')
233 track.instru = 'instru-%s' % track_id in request.POST
234 track.sabam = 'sabam-%s' % track_id in request.POST
235 track.cfwb = 'cfwb-%s' % track_id in request.POST
237 return HttpResponseRedirect('.')
240 class QuickLinksView(TemplateView):
241 template_name = 'nonstop/quick_links.html'
244 class SearchView(TemplateView):
245 template_name = 'nonstop/search.html'
247 def get_context_data(self, **kwargs):
248 ctx = super(SearchView, self).get_context_data(**kwargs)
249 ctx['form'] = TrackSearchForm()
250 queryset = Track.objects.all()
252 q = self.request.GET.get('q')
254 queryset = queryset.filter(Q(title__icontains=q.lower()) | Q(artist__name__icontains=q.lower()))
256 zone = self.request.GET.get('zone')
258 from emissions.models import Nonstop
259 queryset = queryset.filter(nonstop_zones=zone)
262 ctx['tracks'] = queryset.order_by('title').select_related()
267 class CleanupView(TemplateView):
268 template_name = 'nonstop/cleanup.html'
270 def get_context_data(self, **kwargs):
271 ctx = super(CleanupView, self).get_context_data(**kwargs)
272 ctx['form'] = CleanupForm()
274 zone = self.request.GET.get('zone')
276 from emissions.models import Nonstop
277 ctx['zone'] = Nonstop.objects.get(id=zone)
278 ctx['count'] = Track.objects.filter(nonstop_zones=zone).count()
279 ctx['tracks'] = Track.objects.filter(nonstop_zones=zone).order_by(
280 'added_to_nonstop_timestamp').select_related()[:30]
283 def post(self, request, *args, **kwargs):
285 for track_id in request.POST.getlist('track'):
286 if request.POST.get('remove-%s' % track_id):
287 track = Track.objects.get(id=track_id)
288 track.nonstop_zones.clear()
289 track.sync_nonstop_zones()
292 messages.info(self.request, 'Removed %d new track(s)' % count)
293 return HttpResponseRedirect('.')