source: telemeta/web/base.py @ 16360cc

cremcrem2devdev2diademsdj1.6feature/breadcrumbsfeature/ts-0.5feature/ts-0.5.4feature/writecacheformagenericinstru_searchlamlam2mapsv3mergenlivemultiproductionrelease/1.4.4sabiodsecurityserversocialstoragetelecastertestvideo
Last change on this file since 16360cc was 16360cc, checked in by yomguy <yomguy@…>, 3 years ago

clean collection copy method, timeside watermark

  • Property mode set to 100644
File size: 49.8 KB
Line 
1# -*- coding: utf-8 -*-
2# Copyright (C) 2007-2010 Samalyse SARL
3# Copyright (C) 2010-2011 Parisson SARL
4
5# This software is a computer program whose purpose is to backup, analyse,
6# transcode and stream any audio content with its metadata over a web frontend.
7
8# This software is governed by the CeCILL  license under French law and
9# abiding by the rules of distribution of free software.  You can  use,
10# modify and/ or redistribute the software under the terms of the CeCILL
11# license as circulated by CEA, CNRS and INRIA at the following URL
12# "http://www.cecill.info".
13
14# As a counterpart to the access to the source code and  rights to copy,
15# modify and redistribute granted by the license, users are provided only
16# with a limited warranty  and the software's author,  the holder of the
17# economic rights,  and the successive licensors  have only  limited
18# liability.
19
20# In this respect, the user's attention is drawn to the risks associated
21# with loading,  using,  modifying and/or developing or reproducing the
22# software by the user in light of its specific status of free software,
23# that may mean  that it is complicated to manipulate,  and  that  also
24# therefore means  that it is reserved for developers  and  experienced
25# professionals having in-depth computer knowledge. Users are therefore
26# encouraged to load and test the software's suitability as regards their
27# requirements in conditions enabling the security of their systems and/or
28# data to be ensured and,  more generally, to use and operate it in the
29# same conditions as regards security.
30
31# The fact that you are presently reading this means that you have had
32# knowledge of the CeCILL license and that you accept its terms.
33
34# Authors: Olivier Guilyardi <olivier@samalyse.com>
35#          Guillaume Pellerin <yomguy@parisson.com>
36
37import re
38import os
39import sys
40import csv
41import time
42import datetime
43import timeside
44
45from jsonrpc import jsonrpc_method
46
47from django.utils.decorators import method_decorator
48from django.contrib.auth import authenticate, login
49from django.template import RequestContext, loader
50from django import template
51from django.http import HttpResponse, HttpResponseRedirect
52from django.http import Http404
53from django.shortcuts import render_to_response, redirect
54from django.views.generic import list_detail
55from django.conf import settings
56from django.contrib import auth
57from django.contrib import messages
58from django.contrib.auth.decorators import login_required, permission_required
59from django.core.context_processors import csrf
60from django.forms.models import modelformset_factory, inlineformset_factory
61from django.contrib.auth.models import User
62from django.utils.translation import ugettext
63from django.contrib.auth.forms import UserChangeForm
64from django.core.exceptions import ObjectDoesNotExist
65
66from telemeta.models import *
67import telemeta.models
68import telemeta.interop.oai as oai
69from telemeta.interop.oaidatasource import TelemetaOAIDataSource
70from telemeta.util.unaccent import unaccent
71from telemeta.util.unaccent import unaccent_icmp
72from telemeta.util.logger import Logger
73from telemeta.util.unicode import UnicodeWriter
74from telemeta.util.PyRSS2Gen import *
75from telemeta.cache import TelemetaCache
76import telemeta.web.pages as pages
77
78
79def render(request, template, data = None, mimetype = None):
80    return render_to_response(template, data, context_instance=RequestContext(request), 
81                              mimetype=mimetype)
82
83def stream_from_processor(__decoder, __processor, __flag):
84    while True:
85        __frames, eodproc = __processor.process(*__decoder.process())
86        if eodproc:
87            __flag.value = True
88            __flag.save()
89            break
90        yield __processor.chunk
91
92def stream_from_file(file):
93    chunk_size = 0x10000
94    f = open(file, 'r')
95    while True:
96        __chunk = f.read(chunk_size)
97        if not len(__chunk):
98            f.close()
99            break
100        yield __chunk
101   
102   
103class WebView(object):
104    """Provide web UI methods"""
105
106    graphers = timeside.core.processors(timeside.api.IGrapher)
107    decoders = timeside.core.processors(timeside.api.IDecoder)
108    encoders = timeside.core.processors(timeside.api.IEncoder)
109    analyzers = timeside.core.processors(timeside.api.IAnalyzer)
110    cache_data = TelemetaCache(settings.TELEMETA_DATA_CACHE_DIR)
111    cache_export = TelemetaCache(settings.TELEMETA_EXPORT_CACHE_DIR)
112   
113    def index(self, request):
114        """Render the homepage"""
115        if not request.user.is_authenticated():
116            template = loader.get_template('telemeta/index.html')
117            ids = [id for id in MediaItem.objects.all().values_list('id', flat=True).order_by('?')[0:3]]
118            items = MediaItem.objects.enriched().filter(pk__in=ids)
119            revisions = self.get_revisions(request, 3)
120            context = RequestContext(request, {
121                        'page_content': pages.get_page_content(request, 'home', ignore_slash_issue=True),
122                        'items': items, 'revisions': revisions})
123            return HttpResponse(template.render(context))
124        else:
125            template='telemeta/home.html'
126            playlists = self.get_playlists(request)
127            revisions = self.get_revisions(request, 15)
128            searches = Search.objects.filter(username=request.user)
129            return render(request, template, {'playlists': playlists, 'searches': searches, 
130                                              'revisions': revisions,})
131 
132    def get_revisions(self, request, nb):
133        last_revisions = Revision.objects.all().order_by('-time')[0:nb]
134        revisions = []
135        for revision in last_revisions:
136            if revision.element_type == 'item':
137                try:
138                    element = MediaItem.objects.get(pk=revision.element_id)
139                except:
140                    element = None
141            if revision.element_type == 'collection':
142                try:
143                    element = MediaCollection.objects.get(pk=revision.element_id)
144                except:
145                    element = None
146            if revision.element_type == 'marker':
147                try:
148                    element = MediaItemMarker.objects.get(pk=revision.element_id)
149                except:
150                    element = None
151            revisions.append({'revision': revision, 'element': element})
152       
153        return revisions
154       
155    def collection_detail(self, request, public_id, template='telemeta/collection_detail.html'):
156        collection = MediaCollection.objects.get(public_id=public_id)
157        if collection.public_access == 'none' and not request.user.is_staff:
158            return HttpResponseRedirect('not_allowed/')
159        playlists = self.get_playlists(request)
160        return render(request, template, {'collection': collection, 'playlists' : playlists})
161
162    @method_decorator(permission_required('telemeta.change_mediacollection'))
163    def collection_edit(self, request, public_id, template='telemeta/collection_edit.html'):
164        collection = MediaCollection.objects.get(public_id=public_id)
165        if request.method == 'POST':
166            form = MediaCollectionForm(data=request.POST, files=request.FILES, instance=collection)
167            if form.is_valid():
168                code = form.cleaned_data['code']
169                if not code:
170                    code = public_id
171                form.save()
172                collection.set_revision(request.user)
173                return HttpResponseRedirect('/collections/'+code)
174        else:
175            form = MediaCollectionForm(instance=collection)
176       
177        return render(request, template, {'collection': collection, "form": form,})
178
179    @method_decorator(permission_required('telemeta.add_mediacollection'))
180    def collection_add(self, request, template='telemeta/collection_add.html'):
181        collection = MediaCollection()
182        if request.method == 'POST':
183            form = MediaCollectionForm(data=request.POST, files=request.FILES, instance=collection)
184            if form.is_valid():
185                code = form.cleaned_data['code']
186                if not code:
187                    code = public_id
188                form.save()
189                collection.set_revision(request.user)
190                return HttpResponseRedirect('/collections/'+code)
191        else:
192            form = MediaCollectionForm(instance=collection)
193       
194        return render(request, template, {'collection': collection, "form": form,})
195
196    @method_decorator(permission_required('telemeta.add_mediacollection'))
197    def collection_copy(self, request, public_id, template='telemeta/collection_edit.html'):
198        if request.method == 'POST':
199            new_collection = MediaCollection()
200            form = MediaCollectionForm(data=request.POST, files=request.FILES, instance=new_collection)
201            if form.is_valid():
202                code = form.cleaned_data['code']
203                if not code:
204                    code = public_id
205                form.save()
206                new_collection.set_revision(request.user)
207                return HttpResponseRedirect('/collections/'+code)
208        else:
209            collection = MediaCollection.objects.get(public_id=public_id)
210            form = MediaCollectionForm(instance=collection)
211       
212        return render(request, template, {'collection': collection, "form": form,})
213       
214    def item_previous_next(self, item):
215        # Get previous and next items
216        pks = []
217        items = MediaItem.objects.filter(collection=item.collection)
218        if len(items) > 1:
219            for it in items:
220                pks.append(it.pk)
221            pks.sort()
222            for pk in pks:
223                if pk == item.pk:
224                    if pk == pks[0]:
225                        previous_pk = pks[-1]
226                        next_pk = pks[1]
227                    elif pk == pks[-1]:
228                        previous_pk = pks[-2]
229                        next_pk = pks[0]
230                    else:
231                        previous_pk = pks[pks.index(pk)-1]
232                        next_pk = pks[pks.index(pk)+1]
233                    for it in items:
234                        if it.pk == previous_pk:
235                            previous = it
236                        if it.pk == next_pk:
237                            next = it
238                    previous = previous.public_id
239                    next = next.public_id
240        else:
241             previous = item.public_id   
242             next = item.public_id
243       
244        return previous, next
245       
246    def item_detail(self, request, public_id=None, marker_id=None, template='telemeta/mediaitem_detail.html'):
247        """Show the details of a given item"""
248       
249        if not public_id and marker_id:
250            marker = MediaItemMarker.objects.get(public_id=marker_id)
251            item_id = marker.item_id
252            item = MediaItem.objects.get(id=item_id)
253        else:
254            item = MediaItem.objects.get(public_id=public_id)
255       
256        if item.public_access == 'none' and not request.user.is_staff:
257            return HttpResponseRedirect('not_allowed/')
258           
259        # Get TimeSide processors
260        formats = []
261        for encoder in self.encoders:
262            formats.append({'name': encoder.format(), 'extension': encoder.file_extension()})
263
264        graphers = []
265        for grapher in self.graphers:
266            graphers.append({'name':grapher.name(), 'id': grapher.id()})
267        if request.REQUEST.has_key('grapher_id'):
268            grapher_id = request.REQUEST['grapher_id']
269        else:
270            grapher_id = 'waveform'
271       
272        previous, next = self.item_previous_next(item)
273        analyzers = self.item_analyze(item)
274        playlists = self.get_playlists(request)
275        public_access = self.get_public_access(item.public_access, item.recorded_from_date, item.recorded_to_date)
276               
277        return render(request, template,
278                    {'item': item, 'export_formats': formats,
279                    'visualizers': graphers, 'visualizer_id': grapher_id,'analysers': analyzers,
280                    'audio_export_enabled': getattr(settings, 'TELEMETA_DOWNLOAD_ENABLED', True),
281                    'previous' : previous, 'next' : next, 'marker': marker_id, 'playlists' : playlists, 
282                    'public_access': public_access,
283                    })
284   
285    def get_public_access(self, access, date_from, date_to):
286        # Rolling publishing date : public access is given when time between recorded year
287        # and current year is over the settings value PUBLIC_ACCESS_PERIOD
288        if date_to:
289            date = date_to
290        elif date_from:
291            date = date_from
292        else:
293            date = None
294        if access == 'full':
295            public_access = True
296        else:
297            public_access = False
298        if date:
299            year = str(date).split('-')
300            year_now = datetime.datetime.now().strftime("%Y")
301            if int(year_now) - int(year[0]) >= settings.TELEMETA_PUBLIC_ACCESS_PERIOD:
302                public_access = True
303       
304        return public_access
305       
306    @method_decorator(permission_required('telemeta.change_mediaitem'))
307    def item_edit(self, request, public_id, template='telemeta/mediaitem_edit.html'):
308        """Show the details of a given item"""
309        item = MediaItem.objects.get(public_id=public_id)
310       
311        formats = []
312        for encoder in self.encoders:
313            formats.append({'name': encoder.format(), 'extension': encoder.file_extension()})
314
315        graphers = []
316        for grapher in self.graphers:
317            graphers.append({'name':grapher.name(), 'id': grapher.id()})
318        if request.REQUEST.has_key('grapher_id'):
319            grapher_id = request.REQUEST['grapher_id']
320        else:
321            grapher_id = 'waveform'
322       
323        previous, next = self.item_previous_next(item)
324        analyzers = self.item_analyze(item)
325       
326        if request.method == 'POST':
327            form = MediaItemForm(data=request.POST, files=request.FILES, instance=item)
328            if form.is_valid():
329                code = form.cleaned_data['code']
330                if not code:
331                    code = public_id
332                form.save()
333                if form.files:
334                    self.cache_data.delete_item_data(code)
335                    self.cache_export.delete_item_data(code)
336                    flags = MediaItemTranscodingFlag.objects.filter(item=item)
337                    for flag in flags:
338                        flag.delete()
339                item.set_revision(request.user)
340                return HttpResponseRedirect('/items/'+code)
341        else:
342            form = MediaItemForm(instance=item)
343       
344        return render(request, template, 
345                    {'item': item, 'export_formats': formats, 
346                    'visualizers': graphers, 'visualizer_id': grapher_id,'analysers': analyzers,
347                    'audio_export_enabled': getattr(settings, 'TELEMETA_DOWNLOAD_ENABLED', True), "form": form, 
348                    'previous' : previous, 'next' : next, 
349                    })
350       
351    @method_decorator(permission_required('telemeta.add_mediaitem'))
352    def item_add(self, request, public_id=None, template='telemeta/mediaitem_add.html'):
353        """Show the details of a given item"""
354        if public_id:
355            collection = MediaCollection.objects.get(public_id=public_id)
356            item = MediaItem(collection=collection)
357        else:
358            item = MediaItem()
359        if request.method == 'POST':
360            form = MediaItemForm(data=request.POST, files=request.FILES, instance=item)
361            if form.is_valid():
362                code = form.cleaned_data['code']
363                if not code:
364                    code = public_id
365                form.save()
366                item.set_revision(request.user)
367                return HttpResponseRedirect('/items/'+code)
368        else:
369            form = MediaItemForm(instance=item)
370       
371        return render(request, template, {'item': item, 'form': form})
372   
373    @method_decorator(permission_required('telemeta.add_mediaitem'))
374    def item_copy(self, request, public_id, template='telemeta/mediaitem_copy.html'):
375        """Show the details of a given item"""       
376        if request.method == 'POST':
377            new_item = MediaItem()
378            form = MediaItemForm(data=request.POST, files=request.FILES, instance=new_item)
379            if form.is_valid():
380                code = form.cleaned_data['code']
381                if not code:
382                    code = public_id
383                form.save()
384                new_item.set_revision(request.user)
385                return HttpResponseRedirect('/items/'+code)
386        else:
387            item = MediaItem.objects.get(public_id=public_id)
388            form = MediaItemForm(instance=item)
389            form.file = None
390       
391        return render(request, template, {'item': item, "form": form})
392       
393    def item_analyze(self, item):
394        public_id = str(item.public_id)
395        analyze_file = public_id + '.xml'
396       
397        if self.cache_data.exists(analyze_file):
398            analyzers = self.cache_data.read_analyzer_xml(analyze_file)
399            if not item.approx_duration:
400                for analyzer in analyzers:
401                    if analyzer['id'] == 'duration':
402                        value = analyzer['value']
403                        time = value.split(':')
404                        time[2] = time[2].split('.')[0]
405                        time = ':'.join(time)
406                        item.approx_duration = str(time)
407                        item.save()
408        else:     
409            analyzers = []
410            analyzers_sub = []
411            if item.file:
412                decoder  = timeside.decoder.FileDecoder(item.file.path)
413                pipe = decoder
414                for analyzer in self.analyzers:
415                    subpipe = analyzer()
416                    analyzers_sub.append(subpipe)
417                    pipe = pipe | subpipe
418                pipe.run()
419
420                mime_type = decoder.format()
421                analyzers.append({'name': 'Mime type', 'id': 'mime_type', 'unit': '', 'value': mime_type})
422                analyzers.append({'name': 'Channels', 'id': 'channels', 'unit': '', 'value': decoder.channels()})
423               
424                for analyzer in analyzers_sub:
425                    value = analyzer.result()
426                    if analyzer.id() == 'duration':
427                        approx_value = int(round(value))
428                        item.approx_duration = approx_value
429                        try:
430                            item.save()
431                        except:
432                            pass
433                        value = datetime.timedelta(0,value)
434                   
435                    analyzers.append({'name':analyzer.name(),
436                                      'id':analyzer.id(),
437                                      'unit':analyzer.unit(),
438                                      'value':str(value)})
439                 
440                self.cache_data.write_analyzer_xml(analyzers, analyze_file)
441           
442        return analyzers
443   
444    def item_analyze_xml(self, request, public_id):
445        item = MediaItem.objects.get(public_id=public_id)
446        analyze_file = public_id + '.xml'
447        if not self.cache_data.exists(analyze_file):
448            self.item_analyze(item)
449        mime_type = 'text/xml'
450        response = HttpResponse(self.cache_data.read_stream_bin(analyze_file), mimetype=mime_type)
451        response['Content-Disposition'] = 'attachment; filename='+public_id+'.xml'       
452        return response       
453       
454    def item_visualize(self, request, public_id, visualizer_id, width, height):
455        item = MediaItem.objects.get(public_id=public_id)
456        mime_type = 'image/png'
457        grapher_id = visualizer_id
458       
459        for grapher in self.graphers:
460            if grapher.id() == grapher_id:
461                break
462
463        if grapher.id() != grapher_id:
464            raise Http404
465       
466        size = width + '_' + height
467        image_file = '.'.join([public_id, grapher_id, size, 'png'])
468
469        if not self.cache_data.exists(image_file):
470            if item.file:
471                path = self.cache_data.dir + os.sep + image_file
472                decoder  = timeside.decoder.FileDecoder(item.file.path)
473                graph = grapher(width = int(width), height = int(height))
474                pipe = decoder | graph
475                pipe.run()
476                graph.watermark('timeside', opacity=.6, margin=(5,5))
477                f = open(path, 'w')
478                graph.render(path)
479                f.close()
480               
481        response = HttpResponse(self.cache_data.read_stream_bin(image_file), mimetype=mime_type)
482        return response
483
484    def list_export_extensions(self):
485        "Return the recognized item export file extensions, as a list"
486        list = []
487        for encoder in self.encoders:
488            list.append(encoder.file_extension())
489        return list
490
491    def item_export(self, request, public_id, extension):                   
492        """Export a given media item in the specified format (OGG, FLAC, ...)"""
493       
494        item = MediaItem.objects.get(public_id=public_id)
495       
496        public_access = self.get_public_access(item.public_access, item.recorded_from_date, item.recorded_to_date)
497        if (not public_access or not extension in settings.TELEMETA_STREAMING_FORMATS) and not request.user.is_staff:
498            return HttpResponseRedirect('not_allowed/')
499
500        for encoder in self.encoders:
501            if encoder.file_extension() == extension:
502                break
503
504        if encoder.file_extension() != extension:
505            raise Http404('Unknown export file extension: %s' % extension)
506
507        mime_type = encoder.mime_type()
508        file = public_id + '.' + encoder.file_extension()
509        audio = item.file.path
510       
511        flag = MediaItemTranscodingFlag.objects.filter(item=item, mime_type=mime_type)
512        if not flag:
513            flag = MediaItemTranscodingFlag(item=item, mime_type=mime_type)
514            flag.value = False
515            flag.save()
516        else:
517            flag = flag[0]
518       
519        analyzers = self.item_analyze(item)
520        if analyzers:
521            for analyzer in analyzers:
522                if analyzer['id'] == 'mime_type':
523                    format = analyzer['value']
524        else:
525            decoder = timeside.decoder.FileDecoder(audio)
526            format = decoder.format()
527       
528        if mime_type in format:
529            # source > stream
530            response = HttpResponse(stream_from_file(audio), mimetype = mime_type)
531           
532        else:
533            dc_metadata = dublincore.express_item(item).to_list()
534            mapping = DublinCoreToFormatMetadata(extension)
535            metadata = mapping.get_metadata(dc_metadata)     
536            media = self.cache_export.dir + os.sep + file
537            if not self.cache_export.exists(file) or flag.value == False:
538                # source > encoder > stream
539                decoder = timeside.decoder.FileDecoder(audio)
540                decoder.setup()
541                proc = encoder(media, streaming=True)
542                proc.setup(channels=decoder.channels(), samplerate=decoder.samplerate())
543                proc.set_metadata(metadata)
544                response = HttpResponse(stream_from_processor(decoder, proc, flag), mimetype = mime_type)
545            else:
546                # cache > stream
547                proc = encoder(media)
548                proc.set_metadata(metadata)
549                proc.write_metadata()
550                response = HttpResponse(self.cache_export.read_stream_bin(file), mimetype = mime_type)
551       
552        response['Content-Disposition'] = 'attachment'
553        return response
554
555    def edit_search(self, request, criteria=None):
556        year_min, year_max = MediaCollection.objects.all().recording_year_range()
557        rec_years = year_min and year_max and range(year_min, year_max + 1) or []
558        year_min, year_max = MediaCollection.objects.all().publishing_year_range()
559        pub_years = year_min and year_max and range(year_min, year_max + 1) or []
560        return render(request, 'telemeta/search_criteria.html', {
561            'rec_years': rec_years,
562            'pub_years': pub_years,
563            'ethnic_groups': MediaItem.objects.all().ethnic_groups(),
564            'criteria': criteria
565        })
566
567    def complete_location(self, request, with_items=True):
568        input = request.REQUEST
569       
570        token = input['q']
571        limit = int(input['limit'])
572        if with_items:
573            locations = MediaItem.objects.all().locations()
574        else:
575            locations = Location.objects.all()
576
577        locations = locations.filter(name__istartswith=token).order_by('name')[:limit]
578        data = [unicode(l) + " (%d items)" % l.items().count() for l in locations]
579
580        return HttpResponse("\n".join(data))
581
582    def search(self, request, type = None):
583        """Perform a search through collections and items metadata"""
584        collections = MediaCollection.objects.enriched()
585        items = MediaItem.objects.enriched()
586        input = request.REQUEST
587        criteria = {}
588
589        switch = {
590            'pattern': lambda value: ( 
591                collections.quick_search(value), 
592                items.quick_search(value)),
593            'title': lambda value: (
594                collections.word_search('title', value), 
595                items.by_title(value)),
596            'location': lambda value: (
597                collections.by_location(Location.objects.get(name=value)), 
598                items.by_location(Location.objects.get(name=value))),
599            'continent': lambda value: (
600                collections.by_continent(value), 
601                items.filter(continent = value)),
602            'ethnic_group': lambda value: (
603                collections.by_ethnic_group(value), 
604                items.filter(ethnic_group = value),
605                EthnicGroup.objects.get(pk=value)),
606            'creator': lambda value: (
607                collections.word_search('creator', value),
608                items.word_search('collection__creator', value)),
609            'collector': lambda value: (
610                collections.by_fuzzy_collector(value),
611                items.by_fuzzy_collector(value)),
612            'rec_year_from': lambda value: (
613                collections.by_recording_year(int(value), int(input.get('rec_year_to', value))), 
614                items.by_recording_date(datetime.date(int(value), 1, 1), 
615                                        datetime.date(int(input.get('rec_year_to', value)), 12, 31))),
616            'rec_year_to': lambda value: (collections, items),
617            'pub_year_from': lambda value: (
618                collections.by_publish_year(int(value), int(input.get('pub_year_to', value))), 
619                items.by_publish_year(int(value), int(input.get('pub_year_to', value)))),
620            'pub_year_to': lambda value: (collections, items),
621        }
622       
623        for key, value in input.items():
624            func = switch.get(key)
625            if func and value and value != "0":
626                try:
627                    res = func(value)
628                    if len(res)  > 2:
629                        collections, items, value = res
630                    else: 
631                        collections, items = res
632                except ObjectDoesNotExist:
633                    collections = collections.none()
634                    items = items.none()
635
636                criteria[key] = value
637
638        if type is None:
639            if collections.count():
640                type = 'collections'
641            else:
642                type = 'items'
643
644        if type == 'items':
645            objects = items
646        else:
647            objects = collections
648
649        return list_detail.object_list(request, objects, 
650            template_name='telemeta/search_results.html', paginate_by=20,
651            extra_context={'criteria': criteria, 'collections_num': collections.count(), 
652                'items_num': items.count(), 'type' : type})
653
654    # ADMIN
655    @method_decorator(permission_required('sites.change_site'))
656    def admin_index(self, request):
657        return render(request, 'telemeta/admin.html', self.__get_admin_context_vars())
658
659    @method_decorator(permission_required('sites.change_site'))
660    def admin_general(self, request):
661        return render(request, 'telemeta/admin_general.html', self.__get_admin_context_vars())
662   
663    @method_decorator(permission_required('sites.change_site'))
664    def admin_enumerations(self, request):
665        return render(request, 'telemeta/admin_enumerations.html', self.__get_admin_context_vars())
666    @method_decorator(permission_required('sites.change_site'))
667    def admin_users(self, request):
668        users = User.objects.all()
669        return render(request, 'telemeta/admin_users.html', {'users': users})
670
671    # ENUMERATIONS
672    def __get_enumerations_list(self):
673        from django.db.models import get_models
674        models = get_models(telemeta.models)
675
676        enumerations = []
677        for model in models:
678            if issubclass(model, Enumeration):
679                enumerations.append({"name": model._meta.verbose_name, 
680                    "id": model._meta.module_name})
681
682        cmp = lambda obj1, obj2: unaccent_icmp(obj1['name'], obj2['name'])
683        enumerations.sort(cmp)
684        return enumerations                   
685   
686    def __get_admin_context_vars(self):
687        return {"enumerations": self.__get_enumerations_list()}
688   
689    def __get_enumeration(self, id):
690        from django.db.models import get_models
691        models = get_models(telemeta.models)
692        for model in models:
693            if model._meta.module_name == id:
694                break
695
696        if model._meta.module_name != id:
697            return None
698
699        return model
700
701    @method_decorator(permission_required('telemeta.change_keyword'))
702    def edit_enumeration(self, request, enumeration_id):       
703
704        enumeration  = self.__get_enumeration(enumeration_id)
705        if enumeration == None:
706            raise Http404
707
708        vars = self.__get_admin_context_vars()
709        vars["enumeration_id"] = enumeration._meta.module_name
710        vars["enumeration_name"] = enumeration._meta.verbose_name           
711        vars["enumeration_values"] = enumeration.objects.all()
712        return render(request, 'telemeta/enumeration_edit.html', vars)
713
714    @method_decorator(permission_required('telemeta.add_keyword'))
715    def add_to_enumeration(self, request, enumeration_id):       
716
717        enumeration  = self.__get_enumeration(enumeration_id)
718        if enumeration == None:
719            raise Http404
720
721        enumeration_value = enumeration(value=request.POST['value'])
722        enumeration_value.save()
723
724        return self.edit_enumeration(request, enumeration_id)
725
726    @method_decorator(permission_required('telemeta.change_keyword'))
727    def update_enumeration(self, request, enumeration_id):       
728       
729        enumeration  = self.__get_enumeration(enumeration_id)
730        if enumeration == None:
731            raise Http404
732       
733        if request.method == 'POST':
734            enumeration.objects.filter(id__in=request.POST.getlist('sel')).delete()
735
736        return self.edit_enumeration(request, enumeration_id)
737
738    @method_decorator(permission_required('telemeta.change_keyword'))
739    def edit_enumeration_value(self, request, enumeration_id, value_id):       
740
741        enumeration  = self.__get_enumeration(enumeration_id)
742        if enumeration == None:
743            raise Http404
744       
745        vars = self.__get_admin_context_vars()
746        vars["enumeration_id"] = enumeration._meta.module_name
747        vars["enumeration_name"] = enumeration._meta.verbose_name           
748        vars["enumeration_record"] = enumeration.objects.get(id__exact=value_id)
749        return render(request, 'telemeta/enumeration_edit_value.html', vars)
750
751    @method_decorator(permission_required('telemeta.change_keyword'))
752    def update_enumeration_value(self, request, enumeration_id, value_id):       
753
754        if request.method == 'POST':
755            enumeration  = self.__get_enumeration(enumeration_id)
756            if enumeration == None:
757                raise Http404
758       
759            record = enumeration.objects.get(id__exact=value_id)
760            record.value = request.POST["value"]
761            record.save()
762
763        return self.edit_enumeration(request, enumeration_id)
764 
765
766    # INSTRUMENTS
767    @method_decorator(permission_required('telemeta.change_instrument'))
768    def edit_instrument(self, request):       
769       
770        instruments = Instrument.objects.all().order_by('name')
771        if instruments == None:
772            raise Http404
773        return render(request, 'telemeta/instrument_edit.html', {'instruments': instruments})
774
775    @method_decorator(permission_required('telemeta.add_instrument'))
776    def add_to_instrument(self, request):       
777
778        if request.method == 'POST':
779            instrument = Instrument(name=request.POST['value'])
780            instrument.save()
781
782        return self.edit_instrument(request)
783
784    @method_decorator(permission_required('telemeta.change_instrument'))
785    def update_instrument(self, request):       
786       
787        if request.method == 'POST':
788            Instrument.objects.filter(id__in=request.POST.getlist('sel')).delete()
789
790        return self.edit_instrument(request)
791
792    @method_decorator(permission_required('telemeta.change_instrument'))
793    def edit_instrument_value(self, request, value_id):       
794        instrument = Instrument.objects.get(id__exact=value_id)
795       
796        return render(request, 'telemeta/instrument_edit_value.html', {'instrument': instrument})
797
798    @method_decorator(permission_required('telemeta.change_instrument'))
799    def update_instrument_value(self, request, value_id):       
800
801        if request.method == 'POST':       
802            instrument = Instrument.objects.get(id__exact=value_id)
803            instrument.name = request.POST["value"]
804            instrument.save()
805
806        return self.edit_instrument(request)
807       
808    def collection_playlist(self, request, public_id, template, mimetype):
809        try:
810            collection = MediaCollection.objects.get(public_id=public_id)
811        except ObjectDoesNotExist:
812            raise Http404
813
814        template = loader.get_template(template)
815        context = RequestContext(request, {'collection': collection, 'host': request.META['HTTP_HOST']})
816        return HttpResponse(template.render(context), mimetype=mimetype)
817
818    def item_playlist(self, request, public_id, template, mimetype):
819        try:
820            item = MediaItem.objects.get(public_id=public_id)
821        except ObjectDoesNotExist:
822            raise Http404
823
824        template = loader.get_template(template)
825        context = RequestContext(request, {'item': item, 'host': request.META['HTTP_HOST']})
826        return HttpResponse(template.render(context), mimetype=mimetype)
827
828    def list_continents(self, request):
829        continents = MediaItem.objects.all().countries(group_by_continent=True)
830        return render(request, 'telemeta/geo_continents.html', 
831                    {'continents': continents, 'gmap_key': settings.TELEMETA_GMAP_KEY })
832
833    def country_info(self, request, id):
834        country = Location.objects.get(pk=id)
835        return render(request, 'telemeta/country_info.html', {
836            'country': country, 'continent': country.continents()[0]})
837
838    def list_countries(self, request, continent):                   
839        continent = Location.objects.by_flatname(continent)[0]
840        countries = MediaItem.objects.by_location(continent).countries()
841
842        return render(request, 'telemeta/geo_countries.html', {
843            'continent': continent,
844            'countries': countries
845        })
846
847    def list_country_collections(self, request, continent, country):
848        continent = Location.objects.by_flatname(continent)[0]
849        country = Location.objects.by_flatname(country)[0]
850        objects = MediaCollection.objects.enriched().by_location(country)
851        return list_detail.object_list(request, objects, 
852            template_name='telemeta/geo_country_collections.html', paginate_by=20,
853            extra_context={'country': country, 'continent': continent})
854
855    def list_country_items(self, request, continent, country):
856        continent = Location.objects.by_flatname(continent)[0]
857        country = Location.objects.by_flatname(country)[0]
858        objects = MediaItem.objects.enriched().by_location(country)
859        return list_detail.object_list(request, objects, 
860            template_name='telemeta/geo_country_items.html', paginate_by=20,
861            extra_context={'country': country, 'continent': continent})
862
863    def handle_oai_request(self, request):
864        url         = 'http://' + request.META['HTTP_HOST'] + request.path
865        datasource  = TelemetaOAIDataSource()
866        admin       = settings.ADMINS[0][1]
867        provider    = oai.DataProvider(datasource, "Telemeta", url, admin)
868        args        = request.GET.copy()
869        args.update(request.POST)
870        return HttpResponse(provider.handle(args), mimetype='text/xml')
871       
872    def render_flatpage(self, request, path):
873        try:
874            content = pages.get_page_content(request, path)
875        except pages.MalformedPagePath:
876            return redirect(request.path + '/')
877
878        if isinstance(content, pages.PageAttachment):
879            return HttpResponse(content, content.mimetype())
880        else:
881            return render(request, 'telemeta/flatpage.html', {'page_content': content })
882
883    def logout(self, request):
884        auth.logout(request)
885        return redirect('telemeta-home')
886
887    #MARKERS
888    @jsonrpc_method('telemeta.add_marker')
889    def add_marker(request, marker):
890        # marker must be a dict
891        if isinstance(marker, dict):
892            item_id = marker['item_id']
893            item = MediaItem.objects.get(id=item_id)
894            m = MediaItemMarker(item=item) 
895            m.public_id = marker['public_id']
896            m.time = float(marker['time'])
897            m.title = marker['title']
898            m.description = marker['description']
899            m.author = User.objects.get(username=marker['author'])
900            m.save()
901            m.set_revision(request.user)
902        else:
903            raise 'Error : Bad marker dictionnary'
904
905    @jsonrpc_method('telemeta.del_marker')
906    def del_marker(request, public_id):
907        m = MediaItemMarker.objects.get(public_id=public_id)
908        m.delete()
909       
910    @jsonrpc_method('telemeta.get_markers')
911    def get_markers(request, item_id):
912        item = MediaItem.objects.get(id=item_id)
913        markers = MediaItemMarker.objects.filter(item=item)
914        list = []
915        for marker in markers:
916            dict = {}
917            dict['public_id'] = marker.public_id
918            dict['time'] = str(marker.time)
919            dict['title'] = marker.title
920            dict['description'] = marker.description
921            dict['author'] = marker.author.username
922            list.append(dict)
923        return list
924
925    @jsonrpc_method('telemeta.update_marker')
926    def update_marker(request, marker):
927        if isinstance(marker, dict):
928            m = MediaItemMarker.objects.get(public_id=marker['public_id'])
929            m.time = float(marker['time'])
930            m.title = marker['title']
931            m.description = marker['description']
932            m.save()
933            m.set_revision(request.user)
934        else:
935            raise 'Error : Bad marker dictionnary'
936 
937    @jsonrpc_method('telemeta.get_marker_id')
938    def get_marker_id(request, public_id):
939        marker = MediaItemMarker.objects.get(public_id=public_id)
940        return marker.id
941   
942    # PLAYLISTS
943    @jsonrpc_method('telemeta.add_playlist')
944    def add_playlist(request, playlist):
945        # playlist must be a dict
946        if isinstance(playlist, dict):
947            m = Playlist()
948            m.public_id = playlist['public_id']
949            m.title = playlist['title']
950            m.description = playlist['description']
951            m.author = request.user
952            m.save()
953        else:
954            raise 'Error : Bad playlist dictionnary'
955
956    @jsonrpc_method('telemeta.del_playlist')
957    def del_playlist(request, public_id):
958        m = Playlist.objects.get(public_id=public_id)
959        m.delete()
960       
961    def get_playlists(self, request, user=None):
962        if not user:
963            user = request.user
964        playlists = []
965        if user.is_authenticated():
966            user_playlists = Playlist.objects.filter(author=user)
967            for playlist in user_playlists:
968                playlist_resources = PlaylistResource.objects.filter(playlist=playlist)
969                resources = []
970                for resource in playlist_resources:
971                    try:
972                        if resource.resource_type == 'item':
973                            element = MediaItem.objects.get(id=resource.resource_id)
974                        if resource.resource_type == 'collection':
975                            element = MediaCollection.objects.get(id=resource.resource_id)
976                        if resource.resource_type == 'marker':
977                            element = MediaItemMarker.objects.get(id=resource.resource_id)
978                    except:
979                        element = None
980                    resources.append({'element': element, 'type': resource.resource_type, 'public_id': resource.public_id })
981                playlists.append({'playlist': playlist, 'resources': resources})
982        return playlists
983       
984    @jsonrpc_method('telemeta.update_playlist')
985    def update_playlist(request, playlist):
986        if isinstance(playlist, dict):
987            m = Playlist.objects.get(public_id=playlist['public_id'])
988            m.title = float(playlist['title'])
989            m.description = playlist['description']
990            m.save()
991        else:
992            raise 'Error : Bad playlist dictionnary'
993 
994    @jsonrpc_method('telemeta.add_playlist_resource')
995    def add_playlist_resource(request, playlist_id, playlist_resource):
996        # playlist_resource must be a dict
997        if isinstance(playlist_resource, dict):
998            m = PlaylistResource()
999            m.public_id = playlist_resource['public_id']
1000            m.playlist = Playlist.objects.get(public_id=playlist_id, author=request.user)
1001            m.resource_type = playlist_resource['resource_type']
1002            m.resource_id = playlist_resource['resource_id']
1003            m.save()
1004        else:
1005            raise 'Error : Bad playlist_resource dictionnary'
1006
1007    @jsonrpc_method('telemeta.del_playlist_resource')
1008    def del_playlist_resource(request, public_id):
1009        m = PlaylistResource.objects.get(public_id=public_id)
1010        m.delete()
1011       
1012
1013    def playlist_csv_export(self, request, public_id, resource_type):
1014        playlist = Playlist.objects.get(public_id=public_id, author=request.user)
1015        resources = PlaylistResource.objects.filter(playlist=playlist)
1016        response = HttpResponse(mimetype='text/csv')
1017        response['Content-Disposition'] = 'attachment; filename='+playlist.title+'_'+resource_type+'.csv'
1018        writer = UnicodeWriter(response)
1019       
1020        elements = []
1021        for resource in resources:
1022            if resource_type == 'items':
1023                if resource.resource_type == 'collection':
1024                    collection = MediaCollection.objects.get(id=resource.resource_id)
1025                    collection_items = MediaItem.objects.filter(collection=collection)
1026                    for item in collection_items:
1027                        elements.append(item)
1028                elif resource.resource_type == 'item':
1029                    item = MediaItem.objects.get(id=resource.resource_id)
1030                    elements.append(item)
1031               
1032            elif resource_type == 'collections':
1033                if resource.resource_type == 'collection':
1034                    collection = MediaCollection.objects.get(id=resource.resource_id)
1035                    elements.append(collection)
1036               
1037        if elements:
1038            element = elements[0].to_dict()
1039            tags = element.keys()
1040            # code and title on the two first column
1041            tags.remove('code')
1042            tags.remove('title')
1043            tags.sort()
1044            tags.insert(0, 'title')
1045            tags.insert(0, 'code')
1046            writer.writerow(tags)
1047           
1048            for element in elements:
1049                data = []
1050                element = element.to_dict()
1051                for tag in tags:
1052                    data.append(element[tag])
1053                writer.writerow(data)
1054        return response
1055       
1056    def rss(self, request):
1057        "Render the RSS feed of last revisions"
1058        rss_item_list = []
1059        organization = settings.TELEMETA_ORGANIZATION
1060        subjects = settings.TELEMETA_SUBJECTS
1061        rss_host = request.META['HTTP_HOST']
1062        date_now = datetime.datetime.now()
1063        revisions = self.get_revisions(request, 50)
1064        tags = ['title', 'description', 'comment']
1065       
1066        for r in revisions:
1067            revision = r['revision']
1068            element = r['element']
1069            if element:
1070                link = 'http://' + rss_host + '/' + revision.element_type + 's/' + str(element.public_id)               
1071                description = ''
1072                dict = element.to_dict()
1073                for tag in dict.keys():
1074                    try:
1075                        value = dict[tag]
1076                        if value != '':
1077                            description += tag + ' : ' + value + '<br />'
1078                    except:
1079                        continue
1080                    if tag == 'title':
1081                        if element.title == '':
1082                            title = str(element.public_id)
1083                        else:
1084                            title = element.title
1085                        title = element.element_type + ' : ' + title
1086                       
1087                rss_item_list.append(RSSItem(
1088                        title = title,
1089                        link = link,
1090                        description = description.encode('utf-8'),
1091                        guid = Guid(link),
1092                        pubDate = revision.time,)
1093                        )
1094                       
1095        rss = RSS2(title = organization + ' - Telemeta - ' + ugettext('Last changes'),
1096                            link = rss_host,
1097                            description = ' '.join([subject.decode('utf-8') for subject in subjects]),
1098                            lastBuildDate = str(date_now),
1099                            items = rss_item_list,)
1100       
1101        feed = rss.to_xml(encoding='utf-8')
1102        response = HttpResponse(feed, mimetype='application/rss+xml')
1103        return response
1104       
1105    def not_allowed(self, request,  public_id = None):
1106        mess = ugettext('Access not allowed') 
1107        title = public_id + ' : ' + mess
1108        description = ugettext('Please login or contact the website administator to get admin or private access.')
1109        messages.error(request, title)
1110        return render(request, 'telemeta/messages.html', {'description' : description})
1111   
1112    @method_decorator(login_required)
1113    def profile_detail(self, request, username, template='telemeta/profile_detail.html'):
1114        user = User.objects.get(username=username)
1115        try:
1116            profile = user.get_profile()
1117        except:
1118            profile = None
1119        playlists = self.get_playlists(request, user)
1120        return render(request, template, {'profile' : profile, 'usr': user, 'playlists': playlists})
1121       
1122    def profile_edit(self, request, username, template='telemeta/profile_edit.html'):
1123        if request.user.is_staff:
1124            user_hidden_fields = ['profile-user', 'user-password', 'user-last_login', 'user-date_joined']
1125        else:
1126            user_hidden_fields = ['user-username', 'user-is_staff', 'profile-user', 'user-is_active', 
1127                         'user-password', 'user-last_login', 'user-date_joined', 'user-groups', 
1128                         'user-user_permissions', 'user-is_superuser', 'profile-expiration_date']
1129       
1130        user = User.objects.get(username=username)
1131        if user != request.user and not request.user.is_staff:
1132            return HttpResponseRedirect('/accounts/'+username+'/not_allowed/')
1133       
1134        try:
1135            profile = user.get_profile()
1136        except:
1137            profile = UserProfile(user=user)
1138           
1139        if request.method == 'POST':
1140            user_form = UserChangeForm(request.POST, instance=user, prefix='user')
1141            profile_form = UserProfileForm(request.POST, instance=profile, prefix='profile')
1142            if user_form.is_valid() and profile_form.is_valid():
1143                user_form.save()
1144                profile_form.save()
1145                return HttpResponseRedirect('/accounts/'+username+'/profile/')
1146        else:
1147            user_form = UserChangeForm(instance=user, prefix='user')
1148            profile_form = UserProfileForm(instance=profile, prefix='profile')
1149            forms = [user_form, profile_form]
1150        return render(request, template, {'forms': forms, 'usr': user, 'user_hidden_fields': user_hidden_fields})
1151
1152    @method_decorator(permission_required('telemeta.change_mediaitem'))
1153    def item_performances_edit(self, request, public_id, template):
1154        item = MediaItem.objects.get(public_id=public_id)
1155        PerformanceFormSet = inlineformset_factory(MediaItem, MediaItemPerformance, form=MediaItemPerformanceForm)
1156        if request.method == 'POST':
1157            formset = PerformanceFormSet(data=request.POST, instance=item)
1158            if formset.is_valid():
1159                formset.save()
1160                return HttpResponseRedirect('/items/'+public_id)
1161        else:
1162            formset = PerformanceFormSet(instance=item)
1163        return render(request, template, {'item': item, 'formset': formset,})
1164   
1165    @method_decorator(permission_required('telemeta.change_mediaitem'))
1166    def item_keywords_edit(self, request, public_id, template):
1167        item = MediaItem.objects.get(public_id=public_id)
1168        FormSet = inlineformset_factory(MediaItem, MediaItemKeyword)
1169        if request.method == 'POST':
1170            formset = FormSet(data=request.POST, instance=item)
1171            if formset.is_valid():
1172                formset.save()
1173                return HttpResponseRedirect('/items/'+public_id)
1174        else:
1175            formset = FormSet(instance=item)
1176        return render(request, template, {'item': item, 'formset': formset,})
1177   
Note: See TracBrowser for help on using the repository browser.