source: telemeta/views/base.py @ 57fd750

cremcrem2devdev2diademsdj1.6feature/breadcrumbsfeature/ts-0.5feature/ts-0.5.4feature/writecachegenericlamlam2mapsv3mergenlivemultirelease/1.4.4sabiodserverstoragetelecaster
Last change on this file since 57fd750 was 57fd750, checked in by yomguy <yomguy@…>, 2 years ago

fix format handling when adding new item

  • Property mode set to 100644
File size: 71.4 KB
Line 
1# -*- coding: utf-8 -*-
2# Copyright (C) 2007-2010 Samalyse SARL
3# Copyright (C) 2010-2012 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 random
43import datetime
44import timeside
45
46from jsonrpc import jsonrpc_method
47
48from django.utils.decorators import method_decorator
49from django.contrib.auth import authenticate, login
50from django.template import RequestContext, loader
51from django import template
52from django.http import HttpResponse, HttpResponseRedirect
53from django.http import Http404
54from django.shortcuts import render_to_response, redirect, get_object_or_404
55from django.views.generic import list_detail
56from django.views.generic import DetailView
57from django.conf import settings
58from django.contrib import auth
59from django.contrib import messages
60from django.contrib.auth.decorators import login_required, permission_required
61from django.core.context_processors import csrf
62from django.forms.models import modelformset_factory, inlineformset_factory
63from django.contrib.auth.models import User
64from django.utils.translation import ugettext
65from django.contrib.auth.forms import UserChangeForm
66from django.core.exceptions import ObjectDoesNotExist
67from django.contrib.syndication.views import Feed
68
69from telemeta.models import *
70import telemeta.models
71import telemeta.interop.oai as oai
72from telemeta.interop.oaidatasource import TelemetaOAIDataSource
73from telemeta.util.unaccent import unaccent
74from telemeta.util.unaccent import unaccent_icmp
75from telemeta.util.logger import Logger
76from telemeta.util.unicode import UnicodeWriter
77from telemeta.cache import TelemetaCache
78import telemeta.views.pages as pages
79from telemeta.forms import *
80
81# Model type definition
82mods = {'item': MediaItem, 'collection': MediaCollection,
83        'corpus': MediaCorpus, 'fonds': MediaFonds, 'marker': MediaItemMarker, }
84
85# TOOLS
86
87def render(request, template, data = None, mimetype = None):
88    return render_to_response(template, data, context_instance=RequestContext(request),
89                              mimetype=mimetype)
90
91def stream_from_processor(__decoder, __processor, __flag, metadata=None):
92    while True:
93        __frames, __eodproc = __processor.process(*__decoder.process())
94        if __eodproc or not len(__frames):
95            if metadata:
96                __processor.set_metadata(metadata)
97                __processor.write_metadata()
98            __flag.value = True
99            __flag.save()
100            break
101        yield __processor.chunk
102
103def stream_from_file(__file):
104    chunk_size = 0x100000
105    f = open(__file, 'r')
106    while True:
107        __chunk = f.read(chunk_size)
108        if not len(__chunk):
109            f.close()
110            break
111        yield __chunk
112
113def get_public_access(access, year_from=None, year_to=None):
114    # Rolling publishing date : public access is given when time between recorded year
115    # and current year is over the settings value PUBLIC_ACCESS_PERIOD
116    if year_from and not year_from == 0:
117        year = year_from
118    elif year_to and not year_to == 0:
119        year = year_to
120    else:
121        year = 0
122    if access == 'full':
123        public_access = True
124    else:
125        public_access = False
126        if year and not year == 'None':
127            year_now = datetime.datetime.now().strftime("%Y")
128            if int(year_now) - int(year) >= settings.TELEMETA_PUBLIC_ACCESS_PERIOD:
129                public_access = True
130        else:
131            public_access = False
132    return public_access
133
134def get_revisions(nb, user=None):
135    last_revisions = Revision.objects.order_by('-time')
136    if user:
137        last_revisions = last_revisions.filter(user=user)
138    last_revisions = last_revisions[0:nb]
139    revisions = []
140
141    for revision in last_revisions:
142        for type in mods.keys():
143            if revision.element_type == type:
144                try:
145                    element = mods[type].objects.get(pk=revision.element_id)
146                except:
147                    element = None
148        if not element == None:
149            revisions.append({'revision': revision, 'element': element})
150    return revisions
151
152def get_playlists(request, user=None):
153    if not user:
154        user = request.user
155    playlists = []
156    if user.is_authenticated():
157        user_playlists = Playlist.objects.filter(author=user)
158        for playlist in user_playlists:
159            playlist_resources = PlaylistResource.objects.filter(playlist=playlist)
160            resources = []
161            for resource in playlist_resources:
162                try:
163                    for type in mods.keys():
164                        if resource.resource_type == type:
165                            element = mods[type].objects.get(id=resource.resource_id)
166                except:
167                    element = None
168                resources.append({'element': element, 'type': resource.resource_type, 'public_id': resource.public_id })
169            playlists.append({'playlist': playlist, 'resources': resources})
170    return playlists
171
172def check_related_media(medias):
173    for media in medias:
174        if not media.mime_type:
175            media.set_mime_type()
176            media.save()
177        if not media.title and media.url:
178            if 'https' in media.url:
179                media.url = media.url.replace('https', 'http')
180            import lxml.etree
181            parser = lxml.etree.HTMLParser()
182            tree = lxml.etree.parse(media.url, parser)
183            try:
184                title = tree.find(".//title").text
185            except:
186                title = media.url
187            media.title = title.replace('\n', '').strip()
188            media.save()
189
190def auto_code(resources, base_code):
191    index = 1
192    while True:
193        code = base_code + '_' + str(index)
194        r = resources.filter(code=code)
195        if not r:
196            break
197        index += 1
198    return code
199
200
201class GeneralView(object):
202    """Provide general web UI methods"""
203
204    def home(self, request):
205        """Render the index page"""
206
207        template = loader.get_template('telemeta/home.html')
208
209        sound_items = MediaItem.objects.sound()
210        _sound_pub_items = []
211        for item in sound_items:
212            if get_public_access(item.public_access,  str(item.recorded_from_date).split('-')[0],
213                                            str(item.recorded_to_date).split('-')[0]):
214                _sound_pub_items.append(item)
215
216        random.shuffle(_sound_pub_items)
217        if len(_sound_pub_items) != 0:
218            sound_pub_item = _sound_pub_items[0]
219        else:
220            sound_pub_item = None
221        if len(_sound_pub_items) == 2:
222            sound_pub_items = [_sound_pub_items[1]]
223        elif len(_sound_pub_items) > 2:
224            sound_pub_items = _sound_pub_items[1:3]
225        else:
226            sound_pub_items = None
227
228        revisions = get_revisions(4)
229        context = RequestContext(request, {
230                    'page_content': pages.get_page_content(request, 'home', ignore_slash_issue=True),
231                    'revisions': revisions,  'sound_pub_items': sound_pub_items,
232                    'sound_pub_item': sound_pub_item })
233        return HttpResponse(template.render(context))
234
235    def lists(self, request):
236        """Render the home page"""
237
238        if request.user.is_authenticated():
239            template='telemeta/lists.html'
240            playlists = get_playlists(request)
241            revisions = get_revisions(100)
242            searches = Search.objects.filter(username=request.user)
243            user_revisions = get_revisions(25, request.user)
244            return render(request, template, {'playlists': playlists, 'searches': searches,
245                                              'revisions': revisions, 'user_revisions': user_revisions })
246        else:
247            template = 'telemeta/messages.html'
248            mess = ugettext('Access not allowed')
249            title = ugettext('Lists') + ' : ' + mess
250            description = ugettext('Please login or contact the website administator to get a private access.')
251            messages.error(request, title)
252            return render(request, template, {'description' : description})
253
254    def edit_search(self, request, criteria=None):
255        year_min, year_max = MediaCollection.objects.all().recording_year_range()
256        rec_years = year_min and year_max and range(year_min, year_max + 1) or []
257        year_min, year_max = MediaCollection.objects.all().publishing_year_range()
258        pub_years = year_min and year_max and range(year_min, year_max + 1) or []
259        if request.user.is_authenticated():
260            searches = Search.objects.filter(username=request.user)
261        else:
262            searches = []
263        return render(request, 'telemeta/search_criteria.html', {
264            'rec_years': rec_years,
265            'pub_years': pub_years,
266            'ethnic_groups': MediaItem.objects.all().ethnic_groups(),
267            'criteria': criteria,
268            'searches': searches,
269        })
270
271    def handle_oai_request(self, request):
272        host = request.META['HTTP_HOST']
273        datasource  = TelemetaOAIDataSource()
274        repository_name = settings.TELEMETA_ORGANIZATION
275        url         = 'http://' + host + request.path
276        admin       = settings.ADMINS[0][1]
277        provider    = oai.DataProvider(datasource, repository_name, url, admin)
278        args        = request.GET.copy()
279        args.update(request.POST)
280        return HttpResponse(provider.handle(args), mimetype='text/xml')
281
282    def render_flatpage(self, request, path):
283        try:
284            content = pages.get_page_content(request, path)
285        except pages.MalformedPagePath:
286            return redirect(request.path + '/')
287
288        if isinstance(content, pages.PageAttachment):
289            return HttpResponse(content, content.mimetype())
290        else:
291            return render(request, 'telemeta/flatpage.html', {'page_content': content })
292
293    def logout(self, request):
294        auth.logout(request)
295        return redirect('telemeta-home')
296
297    def search(self, request, type = None):
298        """Perform a search through collections and items metadata"""
299        collections = MediaCollection.objects.enriched()
300        items = MediaItem.objects.enriched()
301        corpus = MediaCorpus.objects.all()
302        fonds  = MediaFonds.objects.all()
303        input = request.REQUEST
304        criteria = {}
305
306        switch = {
307            'pattern': lambda value: (
308                collections.quick_search(value),
309                items.quick_search(value),
310                corpus.quick_search(value),
311                fonds.quick_search(value),
312                ),
313            'title': lambda value: (
314                collections.word_search('title', value),
315                items.by_title(value),
316                corpus.word_search('title', value),
317                fonds.word_search('title', value)),
318            'location': lambda value: (
319                collections.by_location(Location.objects.get(name=value)),
320                items.by_location(Location.objects.get(name=value))),
321            'continent': lambda value: (
322                collections.by_continent(value),
323                items.filter(continent = value)),
324            'ethnic_group': lambda value: (
325                collections.by_ethnic_group(value),
326                items.filter(ethnic_group = value),
327                EthnicGroup.objects.get(pk=value)),
328            'creator': lambda value: (
329                collections.word_search('creator', value),
330                items.word_search('collection__creator', value)),
331            'collector': lambda value: (
332                collections.by_fuzzy_collector(value),
333                items.by_fuzzy_collector(value)),
334            'rec_year_from': lambda value: (
335                collections.by_recording_year(int(value), int(input.get('rec_year_to', value))),
336                items.by_recording_date(datetime.date(int(value), 1, 1),
337                                        datetime.date(int(input.get('rec_year_to', value)), 12, 31))),
338            'rec_year_to': lambda value: (collections, items),
339            'pub_year_from': lambda value: (
340                collections.by_publish_year(int(value), int(input.get('pub_year_to', value))),
341                items.by_publish_year(int(value), int(input.get('pub_year_to', value)))),
342            'pub_year_to': lambda value: (collections, items),
343            'sound': lambda value: (
344                collections.sound(),
345                items.sound()),
346            'instrument': lambda value: (
347                collections.by_instrument(value),
348                items.by_instrument(value)),
349        }
350
351        for key, value in input.items():
352            func = switch.get(key)
353            if func and value and value != "0":
354                try:
355                    res = func(value)
356                    if len(res)  > 4:
357                        collections, items, corpus, fonds, value = res
358                    elif len(res) == 4:
359                        collections, items, corpus, fonds = res
360                    elif len(res) == 3:
361                        collections, items, value = res
362                        corpus = corpus.none()
363                        fonds = fonds.none()
364                    else:
365                        collections, items = res
366                        corpus = corpus.none()
367                        fonds = fonds.none()
368
369                except ObjectDoesNotExist:
370                    collections = collections.none()
371                    items = items.none()
372                    corpus = corpus.none()
373                    fonds = fonds.none()
374
375                criteria[key] = value
376
377        # Save the search
378        user = request.user
379        if user:
380            if user.is_authenticated():
381                search = Search(username=user)
382                search.save()
383                if criteria:
384                    for key in criteria.keys():
385                        value = criteria[key]
386                        if key == 'ethnic_group':
387                            try:
388                                group = EthnicGroup.objects.get(value=value)
389                                value = group.id
390                            except:
391                                value = ''
392                        criter = Criteria(key=key, value=value)
393                        criter.save()
394                        search.criteria.add(criter)
395                    search.save()
396
397        if type is None:
398            if collections.count():
399                type = 'collections'
400            else:
401                type = 'items'
402
403        if type == 'items':
404            objects = items
405        elif type == 'collections':
406            objects = collections
407        elif type == 'corpus':
408            objects = corpus
409        elif type == 'fonds':
410            objects = fonds
411
412        return list_detail.object_list(request, objects,
413            template_name='telemeta/search_results.html', paginate_by=20,
414            extra_context={'criteria': criteria, 'collections_num': collections.count(),
415                'items_num': items.count(), 'corpus_num': corpus.count(), 'fonds_num': fonds.count(),
416                'type' : type,})
417
418    def complete_location(self, request, with_items=True):
419        input = request.REQUEST
420
421        token = input['q']
422        limit = int(input['limit'])
423        if with_items:
424            locations = MediaItem.objects.all().locations()
425        else:
426            locations = Location.objects.all()
427
428        locations = locations.filter(name__istartswith=token).order_by('name')[:limit]
429        data = [unicode(l) + " (%d items)" % l.items().count() for l in locations]
430
431        return HttpResponse("\n".join(data))
432
433    @method_decorator(login_required)
434    def users(self, request):
435        users = User.objects.all().order_by('last_name')
436        return render(request, 'telemeta/users.html', {'users': users})
437
438class CollectionView(object):
439    """Provide Collections web UI methods"""
440
441    def collection_detail(self, request, public_id, template='telemeta/collection_detail.html'):
442        collection = MediaCollection.objects.get(public_id=public_id)
443        items = collection.items.enriched()
444        items = items.order_by('code', 'old_code')
445
446        if collection.public_access == 'none' and not (request.user.is_staff or request.user.is_superuser):
447            mess = ugettext('Access not allowed')
448            title = ugettext('Collection') + ' : ' + public_id + ' : ' + mess
449            description = ugettext('Please login or contact the website administator to get a private access.')
450            messages.error(request, title)
451            return render(request, 'telemeta/messages.html', {'description' : description})
452
453        public_access = get_public_access(collection.public_access, collection.recorded_from_year,
454                                                collection.recorded_to_year)
455        playlists = get_playlists(request)
456
457        related_media = MediaCollectionRelated.objects.filter(collection=collection)
458        check_related_media(related_media)
459        parents = MediaCorpus.objects.filter(children=collection)
460        revisions = Revision.objects.filter(element_type='collection',
461                                            element_id=collection.id).order_by('-time')
462        if revisions:
463            last_revision = revisions[0]
464        else:
465            last_revision = None
466
467        return render(request, template, {'collection': collection, 'playlists': playlists,
468                'public_access': public_access, 'items': items, 'related_media': related_media,
469                'parents': parents, 'last_revision': last_revision })
470
471    @method_decorator(permission_required('telemeta.change_mediacollection'))
472    def collection_edit(self, request, public_id, template='telemeta/collection_edit.html'):
473        collection = MediaCollection.objects.get(public_id=public_id)
474        if request.method == 'POST':
475            form = MediaCollectionForm(data=request.POST, files=request.FILES, instance=collection)
476            if form.is_valid():
477                code = form.cleaned_data['code']
478                if not code:
479                    code = public_id
480                form.save()
481                collection.set_revision(request.user)
482                return HttpResponseRedirect('/archives/collections/'+code)
483        else:
484            form = MediaCollectionForm(instance=collection)
485
486        return render(request, template, {'collection': collection, "form": form,})
487
488    @method_decorator(permission_required('telemeta.add_mediacollection'))
489    def collection_add(self, request, template='telemeta/collection_add.html'):
490        collection = MediaCollection()
491        if request.method == 'POST':
492            form = MediaCollectionForm(data=request.POST, files=request.FILES, instance=collection)
493            if form.is_valid():
494                code = form.cleaned_data['code']
495                if not code:
496                    code = public_id
497                form.save()
498                collection.set_revision(request.user)
499                return HttpResponseRedirect('/archives/collections/'+code)
500        else:
501            form = MediaCollectionForm(instance=collection)
502
503        return render(request, template, {'collection': collection, "form": form,})
504
505    @method_decorator(permission_required('telemeta.add_mediacollection'))
506    def collection_copy(self, request, public_id, template='telemeta/collection_edit.html'):
507        if request.method == 'POST':
508            collection = MediaCollection()
509            form = MediaCollectionForm(data=request.POST, files=request.FILES, instance=collection)
510            if form.is_valid():
511                code = form.cleaned_data['code']
512                if not code:
513                    code = public_id
514                form.save()
515                collection.set_revision(request.user)
516                return HttpResponseRedirect('/archives/collections/'+code)
517        else:
518            collection = MediaCollection.objects.get(public_id=public_id)
519            form = MediaCollectionForm(instance=collection)
520
521        return render(request, template, {'collection': collection, "form": form,})
522
523    def collection_playlist(self, request, public_id, template, mimetype):
524        try:
525            collection = MediaCollection.objects.get(public_id=public_id)
526        except ObjectDoesNotExist:
527            raise Http404
528
529        template = loader.get_template(template)
530        context = RequestContext(request, {'collection': collection, 'host': request.META['HTTP_HOST']})
531        return HttpResponse(template.render(context), mimetype=mimetype)
532
533    @method_decorator(permission_required('telemeta.delete_mediacollection'))
534    def collection_delete(self, request, public_id):
535        """Delete a given collection"""
536        collection = MediaCollection.objects.get(public_id=public_id)
537        collection.delete()
538        return HttpResponseRedirect('/archives/collections/')
539
540    def related_media_collection_stream(self, request, collection_public_id, media_id):
541        collection = MediaCollection.objects.get(public_id=collection_public_id)
542        media = MediaCollectionRelated.objects.get(collection=collection, id=media_id)
543        response = HttpResponse(stream_from_file(media.file.path), mimetype=media.mime_type)
544#        response['Content-Disposition'] = 'attachment'
545        return response
546
547    @method_decorator(permission_required('telemeta.change_mediacollection'))
548    def related_media_edit(self, request, public_id, template):
549        collection = MediaCollection.objects.get(public_id=public_id)
550        MediaCollectionRelatedFormSet = inlineformset_factory(MediaCollection, MediaCollectionRelated, form=MediaCollectionRelatedForm)
551        if request.method == 'POST':
552            formset = MediaCollectionRelatedFormSet(data=request.POST, files=request.FILES, instance=collection)
553            if formset.is_valid():
554                formset.save()
555                collection.set_revision(request.user)
556                return HttpResponseRedirect('/archives/collections/'+public_id)
557        else:
558            formset = MediaCollectionRelatedFormSet(instance=collection)
559
560        return render(request, template, {'collection': collection, 'formset': formset,})
561
562class ItemView(object):
563    """Provide Collections web UI methods"""
564
565    graphers = timeside.core.processors(timeside.api.IGrapher)
566    decoders = timeside.core.processors(timeside.api.IDecoder)
567    encoders = timeside.core.processors(timeside.api.IEncoder)
568    analyzers = timeside.core.processors(timeside.api.IAnalyzer)
569    cache_data = TelemetaCache(settings.TELEMETA_DATA_CACHE_DIR)
570    cache_export = TelemetaCache(settings.TELEMETA_EXPORT_CACHE_DIR)
571
572    def item_previous_next(self, item):
573        # Get previous and next items
574        pks = []
575        items = MediaItem.objects.filter(collection=item.collection)
576        items = items.order_by('code', 'old_code')
577
578        if len(items) > 1:
579            for it in items:
580                pks.append(it.pk)
581            for pk in pks:
582                if pk == item.pk:
583                    if pk == pks[0]:
584                        previous_pk = pks[-1]
585                        next_pk = pks[1]
586                    elif pk == pks[-1]:
587                        previous_pk = pks[-2]
588                        next_pk = pks[0]
589                    else:
590                        previous_pk = pks[pks.index(pk)-1]
591                        next_pk = pks[pks.index(pk)+1]
592                    for it in items:
593                        if it.pk == previous_pk:
594                            previous = it
595                        if it.pk == next_pk:
596                            next = it
597                    previous = previous.public_id
598                    next = next.public_id
599        else:
600             previous = item.public_id
601             next = item.public_id
602
603        return previous, next
604
605    def item_detail(self, request, public_id=None, marker_id=None, width=None, height=None,
606                        template='telemeta/mediaitem_detail.html'):
607        """Show the details of a given item"""
608
609        if not public_id and marker_id:
610            marker = MediaItemMarker.objects.get(public_id=marker_id)
611            item_id = marker.item_id
612            item = MediaItem.objects.get(id=item_id)
613        else:
614            item = MediaItem.objects.get(public_id=public_id)
615
616        item_public_access = item.public_access != 'none' or item.collection.public_access != 'none'
617        if not item_public_access and not (request.user.is_staff or request.user.is_superuser):
618            mess = ugettext('Access not allowed')
619            title = ugettext('Item') + ' : ' + public_id + ' : ' + mess
620            description = ugettext('Please login or contact the website administator to get a private access.')
621            messages.error(request, title)
622            return render(request, 'telemeta/messages.html', {'description' : description})
623
624        # Get TimeSide processors
625        formats = []
626        for encoder in self.encoders:
627            if settings.TELEMETA_DOWNLOAD_FORMATS:
628                if encoder.file_extension() in settings.TELEMETA_DOWNLOAD_FORMATS:
629                    formats.append({'name': encoder.format(), 'extension': encoder.file_extension()})
630            else:
631                formats.append({'name': encoder.format(), 'extension': encoder.file_extension()})
632
633        graphers = []
634        for grapher in self.graphers:
635            graphers.append({'name':grapher.name(), 'id': grapher.id()})
636        if request.REQUEST.has_key('grapher_id'):
637            grapher_id = request.REQUEST['grapher_id']
638        else:
639            try:
640                grapher_id = settings.TELEMETA_DEFAULT_GRAPHER_ID
641            except:
642                grapher_id = 'waveform'
643
644        previous, next = self.item_previous_next(item)
645        mime_type = self.item_analyze(item)
646        #FIXME: use mimetypes.guess_type
647        if 'quicktime' in mime_type:
648            mime_type = 'video/mp4'
649
650        playlists = get_playlists(request)
651        public_access = get_public_access(item.public_access, str(item.recorded_from_date).split('-')[0],
652                                                str(item.recorded_to_date).split('-')[0])
653
654        related_media = MediaItemRelated.objects.filter(item=item)
655        check_related_media(related_media)
656        revisions = Revision.objects.filter(element_type='item', element_id=item.id).order_by('-time')
657        if revisions:
658            last_revision = revisions[0]
659        else:
660            last_revision = None
661
662        format = ''
663        if Format.objects.filter(item=item):
664            format = item.format.get()
665
666        return render(request, template,
667                    {'item': item, 'export_formats': formats,
668                    'visualizers': graphers, 'visualizer_id': grapher_id,
669                    'audio_export_enabled': getattr(settings, 'TELEMETA_DOWNLOAD_ENABLED', True),
670                    'previous' : previous, 'next' : next, 'marker': marker_id, 'playlists' : playlists,
671                    'public_access': public_access, 'width': width, 'height': height,
672                    'related_media': related_media, 'mime_type': mime_type, 'last_revision': last_revision,
673                    'format': format,
674                    })
675
676    @method_decorator(permission_required('telemeta.change_mediaitem'))
677    def item_edit(self, request, public_id, template='telemeta/mediaitem_edit.html'):
678        """Edit a given item"""
679        item = MediaItem.objects.get(public_id=public_id)
680
681        formats = []
682        for encoder in self.encoders:
683            #FIXME: timeside cannot encode to FLAC and OGG now :'(
684            if encoder.file_extension() != 'ogg' and encoder.file_extension() != 'flac':
685                formats.append({'name': encoder.format(), 'extension': encoder.file_extension()})
686
687        graphers = []
688        for grapher in self.graphers:
689            graphers.append({'name':grapher.name(), 'id': grapher.id()})
690        if request.REQUEST.has_key('grapher_id'):
691            grapher_id = request.REQUEST['grapher_id']
692        else:
693            try:
694                grapher_id = settings.TELEMETA_DEFAULT_GRAPHER_ID
695            except:
696                grapher_id = 'waveform'
697
698        previous, next = self.item_previous_next(item)
699        mime_type = self.item_analyze(item)
700        #FIXME: use mimetypes.guess_type
701        if 'quicktime' in mime_type:
702            mime_type = 'video/mp4'
703
704        format, created = Format.objects.get_or_create(item=item)
705
706        if request.method == 'POST':
707            item_form = MediaItemForm(data=request.POST, files=request.FILES, instance=item, prefix='item')
708            format_form = FormatForm(data=request.POST, instance=format, prefix='format')
709            if item_form.is_valid() and format_form.is_valid():
710                item_form.save()
711                format_form.save()
712                code = item_form.cleaned_data['code']
713                if not code:
714                    code = str(item.id)
715                if item_form.files:
716                    self.cache_data.delete_item_data(code)
717                    self.cache_export.delete_item_data(code)
718                    flags = MediaItemTranscodingFlag.objects.filter(item=item)
719                    analyses = MediaItemAnalysis.objects.filter(item=item)
720                    for flag in flags:
721                        flag.delete()
722                    for analysis in analyses:
723                        analysis.delete()
724                item.set_revision(request.user)
725                return HttpResponseRedirect('/archives/items/'+code)
726        else:
727            item_form = MediaItemForm(instance=item, prefix='item')
728            format_form = FormatForm(instance=format, prefix='format')
729
730        forms = [item_form, format_form]
731        hidden_fields = ['item-copied_from_item', 'format-item']
732
733        return render(request, template,
734                    {'item': item, 'export_formats': formats,
735                    'visualizers': graphers, 'visualizer_id': grapher_id,
736                    'audio_export_enabled': getattr(settings, 'TELEMETA_DOWNLOAD_ENABLED', True),
737                    'forms': forms, 'previous' : previous, 'next' : next, 'mime_type': mime_type,
738                    'hidden_fields': hidden_fields,
739                    })
740
741    def related_media_item_stream(self, request, item_public_id, media_id):
742        item = MediaItem.objects.get(public_id=item_public_id)
743        media = MediaItemRelated.objects.get(item=item, id=media_id)
744        response = HttpResponse(stream_from_file(media.file.path), mimetype=media.mime_type)
745#        response['Content-Disposition'] = 'attachment; '+'filename='+media.title+'.'+ext
746        return response
747
748    @method_decorator(permission_required('telemeta.change_mediaitem'))
749    def related_media_edit(self, request, public_id, template):
750        item = MediaItem.objects.get(public_id=public_id)
751        MediaItemRelatedFormSet = inlineformset_factory(MediaItem, MediaItemRelated, form=MediaItemRelatedForm)
752        if request.method == 'POST':
753            formset = MediaItemRelatedFormSet(data=request.POST, files=request.FILES, instance=item)
754            if formset.is_valid():
755                formset.save()
756                item.set_revision(request.user)
757                return HttpResponseRedirect('/archives/items/'+public_id)
758        else:
759            formset = MediaItemRelatedFormSet(instance=item)
760
761        return render(request, template, {'item': item, 'formset': formset,})
762
763    @method_decorator(permission_required('telemeta.add_mediaitem'))
764    def item_add(self, request, public_id=None, template='telemeta/mediaitem_add.html'):
765        """Add an item"""
766        if public_id:
767            collection = MediaCollection.objects.get(public_id=public_id)
768            items = MediaItem.objects.filter(collection=collection)
769            code = auto_code(items, collection.code)
770            item = MediaItem(collection=collection, code=code)
771            format, created = Format.objects.get_or_create(item=item)
772        else:
773            item = MediaItem()
774            format = Format()
775
776        if request.method == 'POST':
777            item_form = MediaItemForm(data=request.POST, files=request.FILES, instance=item, prefix='item')
778            format_form = FormatForm(data=request.POST, instance=format, prefix='format')
779            if item_form.is_valid() and format_form.is_valid():
780                item_form.save()
781                item.set_revision(request.user)
782                format.item = item
783                format_form.save()
784                code = item_form.cleaned_data['code']
785                if not code:
786                    code = str(item.id)
787                return HttpResponseRedirect('/archives/items/'+code)
788        else:
789            item_form = MediaItemForm(instance=item, prefix='item')
790            format_form = FormatForm(instance=format, prefix='format')
791
792        forms = [item_form, format_form]
793        hidden_fields = ['item-copied_from_item', 'format-item']
794
795        return render(request, template, {'item': item, 'forms': forms, 'hidden_fields': hidden_fields,})
796
797    @method_decorator(permission_required('telemeta.add_mediaitem'))
798    def item_copy(self, request, public_id, template='telemeta/mediaitem_copy.html'):
799        """Copy a given item"""
800        if request.method == 'POST':
801            source_item = MediaItem.objects.get(public_id=public_id)
802            item = MediaItem()
803            format = Format()
804            item_form = MediaItemForm(data=request.POST, files=request.FILES, instance=item, prefix='item')
805            format_form = FormatForm(data=request.POST, instance=format, prefix='format')
806
807            if item_form.is_valid():
808                item_form.save()
809                code = item_form.cleaned_data['code']
810                if not code:
811                    code = str(item.id)
812                if format_form.is_valid():
813                    format.item = item
814                    format_form.save()
815
816                performances = MediaItemPerformance.objects.filter(media_item=source_item)
817                for performance in performances:
818                    performance.pk = None
819                    performance.id = None
820                    performance.media_item = item
821                    performance.save()
822
823                keywords = MediaItemKeyword.objects.filter(item=source_item)
824                for keyword in keywords:
825                    keyword.pk = None
826                    keyword.id = None
827                    keyword.item = item
828                    keyword.save()
829
830                item.set_revision(request.user)
831                return HttpResponseRedirect('/archives/items/'+code)
832        else:
833            item = MediaItem.objects.get(public_id=public_id)
834            items = MediaItem.objects.filter(collection=item.collection)
835            item.code = auto_code(items, item.collection.code)
836            item.approx_duration = ''
837            item_form = MediaItemForm(instance=item, prefix='item')
838            format, created = Format.objects.get_or_create(item=item)
839            format_form = FormatForm(instance=format, prefix='format')
840            item_form.code = item.code
841            item_form.file = None
842
843        forms = [item_form, format_form]
844        hidden_fields = ['item-copied_from_item', 'format-item']
845
846        return render(request, template, {'item': item, "forms": forms, 'hidden_fields': hidden_fields,})
847
848    @method_decorator(permission_required('telemeta.delete_mediaitem'))
849    def item_delete(self, request, public_id):
850        """Delete a given item"""
851        item = MediaItem.objects.get(public_id=public_id)
852        collection = item.collection
853        item.delete()
854        return HttpResponseRedirect('/archives/collections/'+collection.code)
855
856    def item_analyze(self, item):
857        analyses = MediaItemAnalysis.objects.filter(item=item)
858        mime_type = ''
859
860        if analyses:
861            for analysis in analyses:
862                if not item.approx_duration and analysis.analyzer_id == 'duration':
863                    value = analysis.value
864                    time = value.split(':')
865                    time[2] = time[2].split('.')[0]
866                    time = ':'.join(time)
867                    item.approx_duration = time
868                    item.save()
869                if analysis.analyzer_id == 'mime_type':
870                    mime_type = analysis.value
871        else:
872            analyzers = []
873            analyzers_sub = []
874            graphers_sub = []
875
876            if item.file:
877                decoder  = timeside.decoder.FileDecoder(item.file.path)
878                pipe = decoder
879
880                for analyzer in self.analyzers:
881                    subpipe = analyzer()
882                    analyzers_sub.append(subpipe)
883                    pipe = pipe | subpipe
884
885                try:
886                    sizes = settings.TELEMETA_DEFAULT_GRAPHER_SIZES
887                except:
888                    sizes = ['360x130', ]
889
890                for grapher in self.graphers:
891                    for size in sizes:
892                        width = size.split('x')[0]
893                        height = size.split('x')[1]
894                        image_file = '.'.join([item.public_id, grapher.id(), size.replace('x', '_'), 'png'])
895                        path = self.cache_data.dir + os.sep + image_file
896                        graph = grapher(width = int(width), height = int(height))
897                        graphers_sub.append({'graph' : graph, 'path': path})
898                        pipe = pipe | graph
899
900                pipe.run()
901
902                for grapher in graphers_sub:
903                    grapher['graph'].watermark('timeside', opacity=.6, margin=(5,5))
904                    f = open(grapher['path'], 'w')
905                    grapher['graph'].render(grapher['path'])
906                    f.close()
907
908                mime_type = decoder.format()
909                analysis = MediaItemAnalysis(item=item, name='MIME type',
910                                             analyzer_id='mime_type', unit='', value=mime_type)
911                analysis.save()
912                analysis = MediaItemAnalysis(item=item, name='Channels',
913                                             analyzer_id='channels',
914                                             unit='', value=decoder.channels())
915                analysis.save()
916                analysis = MediaItemAnalysis(item=item, name='Samplerate',
917                                             analyzer_id='samplerate', unit='Hz',
918                                             value=unicode(decoder.audiorate))
919                analysis.save()
920                analysis = MediaItemAnalysis(item=item, name='Resolution',
921                                             analyzer_id='resolution', unit='bits',
922                                             value=unicode(decoder.audiowidth))
923                analysis.save()
924                analysis = MediaItemAnalysis(item=item, name='Duration',
925                                             analyzer_id='duration', unit='s',
926                                             value=unicode(datetime.timedelta(0,decoder.duration)))
927                analysis.save()
928
929                for analyzer in analyzers_sub:
930                    value = analyzer.result()
931                    analysis = MediaItemAnalysis(item=item, name=analyzer.name(),
932                                                 analyzer_id=analyzer.id(),
933                                                 unit=analyzer.unit(), value=str(value))
934                    analysis.save()
935
936#                FIXME: parse tags on first load
937#                tags = decoder.tags
938
939        return mime_type
940
941    def item_analyze_xml(self, request, public_id):
942        item = MediaItem.objects.get(public_id=public_id)
943        analyses = MediaItemAnalysis.objects.filter(item=item)
944        analyzers = []
945        for analysis in analyses:
946            analyzers.append(analysis.to_dict())
947        mime_type = 'text/xml'
948        response = HttpResponse(self.cache_data.get_analyzer_xml(analyzers), mimetype=mime_type)
949        response['Content-Disposition'] = 'attachment; filename='+public_id+'.xml'
950        return response
951
952    def item_visualize(self, request, public_id, visualizer_id, width, height):
953        item = MediaItem.objects.get(public_id=public_id)
954        mime_type = 'image/png'
955        grapher_id = visualizer_id
956
957        for grapher in self.graphers:
958            if grapher.id() == grapher_id:
959                break
960
961        if grapher.id() != grapher_id:
962            raise Http404
963
964        size = width + '_' + height
965        image_file = '.'.join([public_id, grapher_id, size, 'png'])
966
967        if not self.cache_data.exists(image_file):
968            if item.file:
969                path = self.cache_data.dir + os.sep + image_file
970                decoder  = timeside.decoder.FileDecoder(item.file.path)
971                graph = grapher(width = int(width), height = int(height))
972                pipe = decoder | graph
973                pipe.run()
974                graph.watermark('timeside', opacity=.6, margin=(5,5))
975                f = open(path, 'w')
976                graph.render(path)
977                f.close()
978
979        response = HttpResponse(self.cache_data.read_stream_bin(image_file), mimetype=mime_type)
980        return response
981
982    def list_export_extensions(self):
983        "Return the recognized item export file extensions, as a list"
984        list = []
985        for encoder in self.encoders:
986            list.append(encoder.file_extension())
987        #FIXME: MP4
988        list.append('mp4')
989        return list
990
991    def item_export(self, request, public_id, extension):
992        """Export a given media item in the specified format (OGG, FLAC, ...)"""
993
994        item = MediaItem.objects.get(public_id=public_id)
995        public_access = get_public_access(item.public_access,
996                                          str(item.recorded_from_date).split('-')[0],
997                                          str(item.recorded_to_date).split('-')[0])
998
999        if (not public_access or not extension in settings.TELEMETA_STREAMING_FORMATS) and \
1000                    not (request.user.has_perm('telemeta.can_play_all_items') or request.user.is_superuser):
1001            mess = ugettext('Access not allowed')
1002            title = 'Item file : ' + public_id + '.' + extension + ' : ' + mess
1003            description = ugettext('Please login or contact the website administator to get a private access.')
1004            messages.error(request, title)
1005            return render(request, 'telemeta/messages.html', {'description' : description})
1006
1007        #FIXME: MP4 handling in TimeSide
1008        if 'mp4' in extension:
1009            mime_type = 'video/mp4'
1010            video = item.file.path
1011            response = HttpResponse(stream_from_file(video), mimetype = mime_type)
1012            response['Content-Disposition'] = 'attachment'
1013            return response
1014
1015        for encoder in self.encoders:
1016            if encoder.file_extension() == extension:
1017                break
1018
1019        if encoder.file_extension() != extension:
1020            raise Http404('Unknown export file extension: %s' % extension)
1021
1022        mime_type = encoder.mime_type()
1023        file = public_id + '.' + encoder.file_extension()
1024        audio = item.file.path
1025
1026        flag = MediaItemTranscodingFlag.objects.filter(item=item, mime_type=mime_type)
1027        if not flag:
1028            flag = MediaItemTranscodingFlag(item=item, mime_type=mime_type)
1029            flag.value = False
1030            flag.save()
1031        else:
1032            flag = flag[0]
1033
1034        format = self.item_analyze(item)
1035        dc_metadata = dublincore.express_item(item).to_list()
1036        mapping = DublinCoreToFormatMetadata(extension)
1037        metadata = mapping.get_metadata(dc_metadata)
1038
1039        if mime_type in format:
1040            # source > stream
1041            if not extension in mapping.unavailable_extensions:
1042                proc = encoder(audio)
1043                proc.set_metadata(metadata)
1044                try:
1045                    proc.write_metadata()
1046                except:
1047                    pass
1048            response = HttpResponse(stream_from_file(audio), mimetype = mime_type)
1049#            fsock = open(audio, 'r')
1050#            response = HttpResponse(fsock, mimetype = mime_type)
1051        else:
1052            media = self.cache_export.dir + os.sep + file
1053            if not self.cache_export.exists(file) or not flag.value:
1054                # source > encoder > stream
1055                decoder = timeside.decoder.FileDecoder(audio)
1056                decoder.setup()
1057                proc = encoder(media, streaming=True)
1058                proc.setup(channels=decoder.channels(), samplerate=decoder.samplerate())
1059                if extension in mapping.unavailable_extensions:
1060                    metadata=None
1061                response = HttpResponse(stream_from_processor(decoder, proc, flag, metadata=metadata), mimetype = mime_type)
1062            else:
1063                # cache > stream
1064                response = HttpResponse(self.cache_export.read_stream_bin(file), mimetype = mime_type)
1065
1066        response['Content-Disposition'] = 'attachment'
1067        return response
1068
1069    def item_playlist(self, request, public_id, template, mimetype):
1070        try:
1071            item = MediaItem.objects.get(public_id=public_id)
1072        except ObjectDoesNotExist:
1073            raise Http404
1074
1075        template = loader.get_template(template)
1076        context = RequestContext(request, {'item': item, 'host': request.META['HTTP_HOST']})
1077        return HttpResponse(template.render(context), mimetype=mimetype)
1078
1079    @method_decorator(permission_required('telemeta.change_mediaitem'))
1080    def item_performances_edit(self, request, public_id, template):
1081        item = MediaItem.objects.get(public_id=public_id)
1082        PerformanceFormSet = inlineformset_factory(MediaItem, MediaItemPerformance, form=MediaItemPerformanceForm)
1083        if request.method == 'POST':
1084            formset = PerformanceFormSet(data=request.POST, instance=item)
1085            if formset.is_valid():
1086                formset.save()
1087                return HttpResponseRedirect('/archives/items/'+public_id)
1088        else:
1089            formset = PerformanceFormSet(instance=item)
1090        return render(request, template, {'item': item, 'formset': formset,})
1091
1092    @method_decorator(permission_required('telemeta.change_mediaitem'))
1093    def item_keywords_edit(self, request, public_id, template):
1094        item = MediaItem.objects.get(public_id=public_id)
1095        FormSet = inlineformset_factory(MediaItem, MediaItemKeyword)
1096        if request.method == 'POST':
1097            formset = FormSet(data=request.POST, instance=item)
1098            if formset.is_valid():
1099                formset.save()
1100                return HttpResponseRedirect('/archives/items/'+public_id)
1101        else:
1102            formset = FormSet(instance=item)
1103        return render(request, template, {'item': item, 'formset': formset,})
1104
1105
1106class AdminView(object):
1107    """Provide Admin web UI methods"""
1108
1109    @method_decorator(permission_required('is_superuser'))
1110    def admin_index(self, request):
1111        return render(request, 'telemeta/admin.html', self.__get_admin_context_vars())
1112
1113    @method_decorator(permission_required('is_superuser'))
1114    def admin_general(self, request):
1115        return render(request, 'telemeta/admin_general.html', self.__get_admin_context_vars())
1116
1117    @method_decorator(permission_required('is_superuser'))
1118    def admin_enumerations(self, request):
1119        return render(request, 'telemeta/admin_enumerations.html', self.__get_admin_context_vars())
1120
1121    @method_decorator(permission_required('is_superuser'))
1122    def admin_users(self, request):
1123        users = User.objects.all()
1124        return render(request, 'telemeta/admin_users.html', {'users': users})
1125
1126    def __get_enumerations_list(self):
1127        from django.db.models import get_models
1128        models = get_models(telemeta.models)
1129
1130        enumerations = []
1131        for model in models:
1132            if issubclass(model, Enumeration):
1133                enumerations.append({"name": model._meta.verbose_name,
1134                    "id": model._meta.module_name})
1135
1136        cmp = lambda obj1, obj2: unaccent_icmp(obj1['name'], obj2['name'])
1137        enumerations.sort(cmp)
1138        return enumerations
1139
1140    def __get_admin_context_vars(self):
1141        return {"enumerations": self.__get_enumerations_list()}
1142
1143    def __get_enumeration(self, id):
1144        from django.db.models import get_models
1145        models = get_models(telemeta.models)
1146        for model in models:
1147            if model._meta.module_name == id:
1148                break
1149
1150        if model._meta.module_name != id:
1151            return None
1152
1153        return model
1154
1155    @method_decorator(permission_required('telemeta.change_keyword'))
1156    def edit_enumeration(self, request, enumeration_id):
1157
1158        enumeration  = self.__get_enumeration(enumeration_id)
1159        if enumeration == None:
1160            raise Http404
1161
1162        vars = self.__get_admin_context_vars()
1163        vars["enumeration_id"] = enumeration._meta.module_name
1164        vars["enumeration_name"] = enumeration._meta.verbose_name
1165        vars["enumeration_values"] = enumeration.objects.all()
1166        return render(request, 'telemeta/enumeration_edit.html', vars)
1167
1168    @method_decorator(permission_required('telemeta.add_keyword'))
1169    def add_to_enumeration(self, request, enumeration_id):
1170
1171        enumeration  = self.__get_enumeration(enumeration_id)
1172        if enumeration == None:
1173            raise Http404
1174
1175        enumeration_value = enumeration(value=request.POST['value'])
1176        enumeration_value.save()
1177
1178        return self.edit_enumeration(request, enumeration_id)
1179
1180    @method_decorator(permission_required('telemeta.change_keyword'))
1181    def update_enumeration(self, request, enumeration_id):
1182
1183        enumeration  = self.__get_enumeration(enumeration_id)
1184        if enumeration == None:
1185            raise Http404
1186
1187        if request.method == 'POST':
1188            enumeration.objects.filter(id__in=request.POST.getlist('sel')).delete()
1189
1190        return self.edit_enumeration(request, enumeration_id)
1191
1192    @method_decorator(permission_required('telemeta.change_keyword'))
1193    def edit_enumeration_value(self, request, enumeration_id, value_id):
1194
1195        enumeration  = self.__get_enumeration(enumeration_id)
1196        if enumeration == None:
1197            raise Http404
1198
1199        vars = self.__get_admin_context_vars()
1200        vars["enumeration_id"] = enumeration._meta.module_name
1201        vars["enumeration_name"] = enumeration._meta.verbose_name
1202        vars["enumeration_record"] = enumeration.objects.get(id__exact=value_id)
1203        return render(request, 'telemeta/enumeration_edit_value.html', vars)
1204
1205    @method_decorator(permission_required('telemeta.change_keyword'))
1206    def update_enumeration_value(self, request, enumeration_id, value_id):
1207
1208        if request.method == 'POST':
1209            enumeration  = self.__get_enumeration(enumeration_id)
1210            if enumeration == None:
1211                raise Http404
1212
1213            record = enumeration.objects.get(id__exact=value_id)
1214            record.value = request.POST["value"]
1215            record.save()
1216
1217        return self.edit_enumeration(request, enumeration_id)
1218
1219
1220class InstrumentView(object):
1221    """Provide Instrument web UI methods"""
1222
1223    @method_decorator(permission_required('telemeta.change_instrument'))
1224    def edit_instrument(self, request):
1225
1226        instruments = Instrument.objects.all().order_by('name')
1227        if instruments == None:
1228            raise Http404
1229        return render(request, 'telemeta/instrument_edit.html', {'instruments': instruments})
1230
1231    @method_decorator(permission_required('telemeta.add_instrument'))
1232    def add_to_instrument(self, request):
1233
1234        if request.method == 'POST':
1235            instrument = Instrument(name=request.POST['value'])
1236            instrument.save()
1237
1238        return self.edit_instrument(request)
1239
1240    @method_decorator(permission_required('telemeta.change_instrument'))
1241    def update_instrument(self, request):
1242
1243        if request.method == 'POST':
1244            Instrument.objects.filter(id__in=request.POST.getlist('sel')).delete()
1245
1246        return self.edit_instrument(request)
1247
1248    @method_decorator(permission_required('telemeta.change_instrument'))
1249    def edit_instrument_value(self, request, value_id):
1250        instrument = Instrument.objects.get(id__exact=value_id)
1251
1252        return render(request, 'telemeta/instrument_edit_value.html', {'instrument': instrument})
1253
1254    @method_decorator(permission_required('telemeta.change_instrument'))
1255    def update_instrument_value(self, request, value_id):
1256
1257        if request.method == 'POST':
1258            instrument = Instrument.objects.get(id__exact=value_id)
1259            instrument.name = request.POST["value"]
1260            instrument.save()
1261
1262        return self.edit_instrument(request)
1263
1264
1265class GeoView(object):
1266    """Provide Geo web UI methods"""
1267
1268    def list_continents(self, request):
1269        continents = MediaItem.objects.all().countries(group_by_continent=True)
1270        return render(request, 'telemeta/geo_continents.html',
1271                    {'continents': continents, 'gmap_key': settings.TELEMETA_GMAP_KEY })
1272
1273    def country_info(self, request, id):
1274        country = Location.objects.get(pk=id)
1275        return render(request, 'telemeta/country_info.html', {
1276            'country': country, 'continent': country.continents()[0]})
1277
1278    def list_countries(self, request, continent):
1279        continent = Location.objects.by_flatname(continent)[0]
1280        countries = MediaItem.objects.by_location(continent).countries()
1281
1282        return render(request, 'telemeta/geo_countries.html', {
1283            'continent': continent,
1284            'countries': countries
1285        })
1286
1287    def list_country_collections(self, request, continent, country):
1288        continent = Location.objects.by_flatname(continent)[0]
1289        country = Location.objects.by_flatname(country)[0]
1290        objects = MediaCollection.objects.enriched().by_location(country)
1291        return list_detail.object_list(request, objects,
1292            template_name='telemeta/geo_country_collections.html', paginate_by=20,
1293            extra_context={'country': country, 'continent': continent})
1294
1295    def list_country_items(self, request, continent, country):
1296        continent = Location.objects.by_flatname(continent)[0]
1297        country = Location.objects.by_flatname(country)[0]
1298        objects = MediaItem.objects.enriched().by_location(country)
1299        return list_detail.object_list(request, objects,
1300            template_name='telemeta/geo_country_items.html', paginate_by=20,
1301            extra_context={'country': country, 'continent': continent})
1302
1303class MarkerView(object):
1304    """Provide Collections web UI methods"""
1305
1306    @jsonrpc_method('telemeta.add_marker')
1307    def add_marker(request, marker):
1308        # marker must be a dict
1309        if isinstance(marker, dict):
1310            item_id = marker['item_id']
1311            item = MediaItem.objects.get(id=item_id)
1312            m = MediaItemMarker(item=item)
1313            m.public_id = marker['public_id']
1314            m.time = float(marker['time'])
1315            m.title = marker['title']
1316            m.description = marker['description']
1317            m.author = User.objects.get(username=marker['author'])
1318            m.save()
1319            m.set_revision(request.user)
1320        else:
1321            raise 'Error : Bad marker dictionnary'
1322
1323    @jsonrpc_method('telemeta.del_marker')
1324    def del_marker(request, public_id):
1325        m = MediaItemMarker.objects.get(public_id=public_id)
1326        m.delete()
1327
1328    @jsonrpc_method('telemeta.get_markers')
1329    def get_markers(request, item_id):
1330        item = MediaItem.objects.get(id=item_id)
1331        markers = MediaItemMarker.objects.filter(item=item)
1332        list = []
1333        for marker in markers:
1334            dict = {}
1335            dict['public_id'] = marker.public_id
1336            dict['time'] = str(marker.time)
1337            dict['title'] = marker.title
1338            dict['description'] = marker.description
1339            dict['author'] = marker.author.username
1340            list.append(dict)
1341        return list
1342
1343    @jsonrpc_method('telemeta.update_marker')
1344    def update_marker(request, marker):
1345        if isinstance(marker, dict):
1346            m = MediaItemMarker.objects.get(public_id=marker['public_id'])
1347            m.time = float(marker['time'])
1348            m.title = marker['title']
1349            m.description = marker['description']
1350            m.save()
1351            m.set_revision(request.user)
1352        else:
1353            raise 'Error : Bad marker dictionnary'
1354
1355    @jsonrpc_method('telemeta.get_marker_id')
1356    def get_marker_id(request, public_id):
1357        marker = MediaItemMarker.objects.get(public_id=public_id)
1358        return marker.id
1359
1360class PlaylistView(object):
1361    """Provide Playlist web UI methods"""
1362
1363    @jsonrpc_method('telemeta.add_playlist')
1364    def add_playlist(request, playlist):
1365        # playlist must be a dict
1366        if isinstance(playlist, dict):
1367            m = Playlist()
1368            m.public_id = playlist['public_id']
1369            m.title = playlist['title']
1370            m.description = playlist['description']
1371            m.author = request.user
1372            m.save()
1373        else:
1374            raise 'Error : Bad playlist dictionnary'
1375
1376    @jsonrpc_method('telemeta.del_playlist')
1377    def del_playlist(request, public_id):
1378        m = Playlist.objects.get(public_id=public_id)
1379        m.delete()
1380
1381    @jsonrpc_method('telemeta.update_playlist')
1382    def update_playlist(request, playlist):
1383        if isinstance(playlist, dict):
1384            m = Playlist.objects.get(public_id=playlist['public_id'])
1385            m.title = playlist['title']
1386            m.description = playlist['description']
1387            m.save()
1388        else:
1389            raise 'Error : Bad playlist dictionnary'
1390
1391    @jsonrpc_method('telemeta.add_playlist_resource')
1392    def add_playlist_resource(request, playlist_id, playlist_resource):
1393        # playlist_resource must be a dict
1394        if isinstance(playlist_resource, dict):
1395            m = PlaylistResource()
1396            m.public_id = playlist_resource['public_id']
1397            m.playlist = Playlist.objects.get(public_id=playlist_id, author=request.user)
1398            m.resource_type = playlist_resource['resource_type']
1399            m.resource_id = playlist_resource['resource_id']
1400            m.save()
1401        else:
1402            raise 'Error : Bad playlist_resource dictionnary'
1403
1404    @jsonrpc_method('telemeta.del_playlist_resource')
1405    def del_playlist_resource(request, public_id):
1406        m = PlaylistResource.objects.get(public_id=public_id)
1407        m.delete()
1408
1409
1410    def playlist_csv_export(self, request, public_id, resource_type):
1411        playlist = Playlist.objects.get(public_id=public_id, author=request.user)
1412        resources = PlaylistResource.objects.filter(playlist=playlist)
1413        response = HttpResponse(mimetype='text/csv')
1414        response['Content-Disposition'] = 'attachment; filename='+playlist.title+'_'+resource_type+'.csv'
1415        writer = UnicodeWriter(response)
1416
1417        elements = []
1418        for resource in resources:
1419            if resource_type == 'items':
1420                if resource.resource_type == 'collection':
1421                    collection = MediaCollection.objects.get(id=resource.resource_id)
1422                    collection_items = MediaItem.objects.filter(collection=collection)
1423                    for item in collection_items:
1424                        elements.append(item)
1425                elif resource.resource_type == 'item':
1426                    item = MediaItem.objects.get(id=resource.resource_id)
1427                    elements.append(item)
1428
1429            elif resource_type == 'collections':
1430                if resource.resource_type == 'collection':
1431                    collection = MediaCollection.objects.get(id=resource.resource_id)
1432                    elements.append(collection)
1433
1434        if elements:
1435            element = elements[0].to_dict()
1436            tags = element.keys()
1437            # code and title on the two first column
1438            tags.remove('code')
1439            tags.remove('title')
1440            tags.sort()
1441            tags.insert(0, 'title')
1442            tags.insert(0, 'code')
1443            writer.writerow(tags)
1444
1445            for element in elements:
1446                data = []
1447                element = element.to_dict()
1448                for tag in tags:
1449                    data.append(element[tag])
1450                writer.writerow(data)
1451        return response
1452
1453
1454class ProfileView(object):
1455    """Provide Collections web UI methods"""
1456
1457    @method_decorator(login_required)
1458    def profile_detail(self, request, username, template='telemeta/profile_detail.html'):
1459        user = User.objects.get(username=username)
1460        try:
1461            profile = user.get_profile()
1462        except:
1463            profile = None
1464        playlists = get_playlists(request, user)
1465        user_revisions = get_revisions(25, user)
1466
1467        return render(request, template, {'profile' : profile, 'usr': user, 'playlists': playlists,
1468                                          'user_revisions': user_revisions})
1469
1470    @method_decorator(login_required)
1471    def profile_edit(self, request, username, template='telemeta/profile_edit.html'):
1472        if request.user.is_superuser:
1473            user_hidden_fields = ['profile-user', 'user-password', 'user-last_login', 'user-date_joined']
1474        else:
1475            user_hidden_fields = ['user-username', 'user-is_staff', 'profile-user', 'user-is_active',
1476                         'user-password', 'user-last_login', 'user-date_joined', 'user-groups',
1477                         'user-user_permissions', 'user-is_superuser', 'profile-expiration_date']
1478
1479        user = User.objects.get(username=username)
1480        if user != request.user and not request.user.is_staff:
1481            mess = ugettext('Access not allowed')
1482            title = ugettext('User profile') + ' : ' + username + ' : ' + mess
1483            description = ugettext('Please login or contact the website administator to get a private access.')
1484            messages.error(request, title)
1485            return render(request, 'telemeta/messages.html', {'description' : description})
1486
1487        try:
1488            profile = user.get_profile()
1489        except:
1490            profile = UserProfile(user=user)
1491
1492        if request.method == 'POST':
1493            user_form = UserChangeForm(request.POST, instance=user, prefix='user')
1494            profile_form = UserProfileForm(request.POST, instance=profile, prefix='profile')
1495            if user_form.is_valid() and profile_form.is_valid():
1496                user_form.save()
1497                profile_form.save()
1498                return HttpResponseRedirect('/accounts/'+username+'/profile/')
1499        else:
1500            user_form = UserChangeForm(instance=user, prefix='user')
1501            profile_form = UserProfileForm(instance=profile, prefix='profile')
1502            forms = [user_form, profile_form]
1503        return render(request, template, {'forms': forms, 'usr': user,
1504                                'user_hidden_fields': user_hidden_fields})
1505
1506
1507class LastestRevisionsFeed(Feed):
1508    "the RSS feed of the lastest revisions"
1509
1510    organization = settings.TELEMETA_ORGANIZATION
1511    subjects = settings.TELEMETA_SUBJECTS
1512    tags = ['title', 'description', 'comment']
1513    title = organization.decode('utf8') + ' - Telemeta - ' + ugettext('Last changes')
1514    link = ""
1515    description = ' '.join([subject.decode('utf-8') for subject in subjects])
1516    n_items = 100
1517
1518    def items(self):
1519        return get_revisions(self.n_items)
1520
1521    def item_title(self, r):
1522        element = r['element']
1523        if element.title == '':
1524            title = str(element.public_id)
1525        else:
1526            title = element.title
1527        return element.element_type + ' : ' + title
1528
1529    def item_description(self, r):
1530        revision = r['revision']
1531        element = r['element']
1532        description = '<b>modified by ' + revision.user.username + ' on ' + unicode(revision.time) + '</b><br /><br />'
1533        dict = element.to_dict()
1534        for tag in dict.keys():
1535            try:
1536                value = dict[tag]
1537                if value != '':
1538                    description += tag + ' : ' + value + '<br />'
1539            except:
1540                continue
1541        return description.encode('utf-8')
1542
1543    def item_link(self, r):
1544        revision = r['revision']
1545        element = r['element']
1546        if revision.element_type[-1] == 's':
1547            dir = revision.element_type
1548        else:
1549            dir = revision.element_type + 's'
1550        link = '/archives/' + dir + '/' + str(element.public_id)
1551        return link
1552
1553
1554class UserRevisionsFeed(LastestRevisionsFeed):
1555
1556    def get_object(self, request, username):
1557        return get_object_or_404(User, username=username)
1558
1559    def items(self, obj):
1560        return get_revisions(self.n_items, obj)
1561
1562
1563class ResourceView(object):
1564    """Provide Resource web UI methods"""
1565
1566    types = {'corpus':
1567                {'model': MediaCorpus,
1568                'form' : MediaCorpusForm,
1569                'related': MediaCorpusRelated,
1570                'related_form': MediaCorpusRelatedForm,
1571                'parent': MediaFonds,
1572                },
1573            'fonds':
1574                {'model': MediaFonds,
1575                'form' : MediaFondsForm,
1576                'related': MediaFondsRelated,
1577                'related_form': MediaFondsRelatedForm,
1578                'parent': None,
1579                }
1580            }
1581
1582    def setup(self, type):
1583        self.model = self.types[type]['model']
1584        self.form = self.types[type]['form']
1585        self.related = self.types[type]['related']
1586        self.related_form = self.types[type]['related_form']
1587        self.parent = self.types[type]['parent']
1588        self.type = type
1589
1590    def detail(self, request, type, public_id, template='telemeta/resource_detail.html'):
1591        self.setup(type)
1592        resource = self.model.objects.get(code=public_id)
1593        children = resource.children.all()
1594        children = children.order_by('code')
1595        related_media = self.related.objects.filter(resource=resource)
1596        check_related_media(related_media)
1597        playlists = get_playlists(request)
1598        revisions = Revision.objects.filter(element_type=type, element_id=resource.id).order_by('-time')
1599        if revisions:
1600            last_revision = revisions[0]
1601        else:
1602            last_revision = None
1603        if self.parent:
1604            parents = self.parent.objects.filter(children=resource)
1605        else:
1606            parents = []
1607
1608        return render(request, template, {'resource': resource, 'type': type, 'children': children,
1609                        'related_media': related_media, 'parents': parents, 'playlists': playlists,
1610                        'last_revision': last_revision })
1611
1612    @jsonrpc_method('telemeta.change_fonds')
1613    @jsonrpc_method('telemeta.change_corpus')
1614    def edit(self, request, type, public_id, template='telemeta/resource_edit.html'):
1615        self.setup(type)
1616        resource = self.model.objects.get(code=public_id)
1617        if request.method == 'POST':
1618            form = self.form(data=request.POST, files=request.FILES, instance=resource)
1619            if form.is_valid():
1620                code = form.cleaned_data['code']
1621                if not code:
1622                    code = public_id
1623                form.save()
1624                resource.set_revision(request.user)
1625                return HttpResponseRedirect('/archives/'+self.type+'/'+code)
1626        else:
1627            form = self.form(instance=resource)
1628        return render(request, template, {'resource': resource, 'type': type, 'form': form,})
1629
1630    @jsonrpc_method('telemeta.add_fonds')
1631    @jsonrpc_method('telemeta.add_corpus')
1632    def add(self, request, type, template='telemeta/resource_add.html'):
1633        self.setup(type)
1634        resource = self.model()
1635        if request.method == 'POST':
1636            form = self.form(data=request.POST, files=request.FILES, instance=resource)
1637            if form.is_valid():
1638                code = form.cleaned_data['code']
1639                if not code:
1640                    code = public_id
1641                form.save()
1642                resource.set_revision(request.user)
1643                return HttpResponseRedirect('/archives/'+self.type +'/'+code)
1644        else:
1645            form = self.form(instance=resource)
1646        return render(request, template, {'resource': resource, 'type': type, 'form': form,})
1647
1648    @jsonrpc_method('telemeta.add_fonds')
1649    @jsonrpc_method('telemeta.add_corpus')
1650    def copy(self, request, type, public_id, template='telemeta/resource_edit.html'):
1651        self.setup(type)
1652        if request.method == 'POST':
1653            resource = self.model()
1654            form = self.form(data=request.POST, files=request.FILES, instance=resource)
1655            if form.is_valid():
1656                code = form.cleaned_data['code']
1657                if not code:
1658                    code = public_id
1659                resource.save()
1660                resource.set_revision(request.user)
1661                return HttpResponseRedirect('/archives/'+self.type +'/'+code)
1662        else:
1663            resource = self.model.objects.get(code=public_id)
1664            form = self.form(instance=resource)
1665        return render(request, template, {'resource': resource, 'type': type, "form": form,})
1666
1667    def playlist(self, request, type, public_id, template, mimetype):
1668        self.setup(type)
1669        try:
1670            resource = self.model.objects.get(code=public_id)
1671        except ObjectDoesNotExist:
1672            raise Http404
1673
1674        template = loader.get_template(template)
1675        context = RequestContext(request, {'resource': resource, 'host': request.META['HTTP_HOST']})
1676        return HttpResponse(template.render(context), mimetype=mimetype)
1677
1678    @jsonrpc_method('telemeta.del_fonds')
1679    @jsonrpc_method('telemeta.del_corpus')
1680    def delete(self, request, type, public_id):
1681        self.setup(type)
1682        resource = self.model.objects.get(code=public_id)
1683        resource.delete()
1684        return HttpResponseRedirect('/archives/'+self.type+'/')
1685
1686    def related_stream(self, request, type, public_id, media_id):
1687        self.setup(type)
1688        resource = self.model.objects.get(code=public_id)
1689        media = self.related.objects.get(resource=resource, id=media_id)
1690        response = HttpResponse(stream_from_file(media.file.path), mimetype=media.mime_type)
1691        return response
1692
1693    @jsonrpc_method('telemeta.add_fonds_related_media')
1694    @jsonrpc_method('telemeta.add_corpus_related_media')
1695    def related_edit(self, request, type, public_id, template):
1696        self.setup(type)
1697        resource = self.model.objects.get(code=public_id)
1698        ResourceRelatedFormSet = inlineformset_factory(self.model, self.related, form=self.related_form)
1699        if request.method == 'POST':
1700            formset = ResourceRelatedFormSet(data=request.POST, files=request.FILES, instance=resource)
1701            if formset.is_valid():
1702                formset.save()
1703                resource.set_revision(request.user)
1704                return HttpResponseRedirect('/archives/'+self.type+'/'+public_id)
1705        else:
1706            formset = ResourceRelatedFormSet(instance=resource)
1707        return render(request, template, {'resource': resource, 'type': type, 'formset': formset,})
1708
1709
Note: See TracBrowser for help on using the repository browser.