source: telemeta/web/base.py @ a0b0f45

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

fix MIME type in DC, fix delete button display, fix JS superuser parsing

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