From 341cbbd319acd2a44de11a71d2484e8a40d9c4f5 Mon Sep 17 00:00:00 2001 From: Guillaume Pellerin Date: Mon, 20 Jan 2014 13:35:38 +0100 Subject: [PATCH] add collection zip get methods, add debug_toolbar to sandbox, jquery 2.0.3 --- example/sandbox/settings.py | 2 + example/sandbox/urls.py | 2 +- setup.py | 2 + telemeta/static/telemeta/css/telemeta.css | 3 + telemeta/static/telemeta/images/zip.png | Bin 0 -> 528 bytes telemeta/templates/telemeta/base.html | 2 +- .../templates/telemeta/collection_detail.html | 5 +- telemeta/urls.py | 13 ++-- telemeta/views/collection.py | 67 +++++++++++++++--- telemeta/views/core.py | 2 +- 10 files changed, 82 insertions(+), 16 deletions(-) create mode 100644 telemeta/static/telemeta/images/zip.png diff --git a/example/sandbox/settings.py b/example/sandbox/settings.py index ddaf6e0a..38a8743a 100644 --- a/example/sandbox/settings.py +++ b/example/sandbox/settings.py @@ -104,6 +104,7 @@ MIDDLEWARE_CLASSES = ( 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.locale.LocaleMiddleware', + 'debug_toolbar.middleware.DebugToolbarMiddleware', ) ROOT_URLCONF = 'sandbox.urls' @@ -130,6 +131,7 @@ INSTALLED_APPS = ( 'south', 'sorl.thumbnail', 'notes', + 'debug_toolbar', ) TEMPLATE_CONTEXT_PROCESSORS = ( diff --git a/example/sandbox/urls.py b/example/sandbox/urls.py index 6dd41922..fb0665ee 100644 --- a/example/sandbox/urls.py +++ b/example/sandbox/urls.py @@ -26,4 +26,4 @@ urlpatterns = patterns('', # Languages (r'^i18n/', include('django.conf.urls.i18n')), (r'^jsi18n/$', 'django.views.i18n.javascript_catalog', js_info_dict), -) + ) diff --git a/setup.py b/setup.py index f61b1255..ec2f9c06 100644 --- a/setup.py +++ b/setup.py @@ -26,6 +26,8 @@ setup( 'psutil', 'pyyaml', 'python-ebml' + 'zipstream', + 'debug_toolbar', ], platforms=['OS Independent'], license='CeCILL v2', diff --git a/telemeta/static/telemeta/css/telemeta.css b/telemeta/static/telemeta/css/telemeta.css index e5c4d387..ba54781c 100644 --- a/telemeta/static/telemeta/css/telemeta.css +++ b/telemeta/static/telemeta/css/telemeta.css @@ -1207,6 +1207,9 @@ a.image-link { .icon_delete{ background-image: url('../images/delete.png'); } +.icon_zip{ + background-image: url('../images/zip.png'); +} .icon_rss,.icon_rss:hover{ background: url('../images/feed-icon-14x14.png') no-repeat; background-position: 0ex .8ex; diff --git a/telemeta/static/telemeta/images/zip.png b/telemeta/static/telemeta/images/zip.png new file mode 100644 index 0000000000000000000000000000000000000000..7e5d49a6a423799ef3c0ef4b6f77605b6a4d893c GIT binary patch literal 528 zcmV+r0`L8aP)O^bC=jBrDYc2bRvjgWIjkatd!dQ+5l zN|$;~mwQl~eo&l$QJjcnoq$rFf>WM|WS@gppNnatg;%19UZaU$qmXc?iCL$LT&av* zs*PT%lyIw;b*-6qt*D)@sh+W^nX{5zvz~sqmtwoJqQ0DGzn^QrpKQOOZ^NN%!=!P< zseQ$!a>cHQ#<-!#r*X}!bT)3oN-zUR)S=hwaG+s5hP((C5i|Nj2}|Nj?@=w|=`00DGTPE!Ct=GbNc z005LpL_t&-(~Zc*8iHUHgyAtzN>oHt!otQ5>~8M=f;>NN)RpgMzGr}6!*6t40>$_I zb=ZkdHeekd=hKR;ulw850)$AmP~UA9+xsY$2@8Pk4sx;bblxvT^F3Dw)Cm!1$EL_a z1(Y%ARG*bL3k8t7#Pod6WFZ4mi)dcl(^yCV-ynwLI|d6L;A%`dlnj=m;~QRX*dsew Sy_~ZE0000P^+ literal 0 HcmV?d00001 diff --git a/telemeta/templates/telemeta/base.html b/telemeta/templates/telemeta/base.html index 32962a2c..c4547b3b 100644 --- a/telemeta/templates/telemeta/base.html +++ b/telemeta/templates/telemeta/base.html @@ -25,7 +25,7 @@ {% block javascript %} - + {% if user.is_authenticated %} diff --git a/telemeta/templates/telemeta/collection_detail.html b/telemeta/templates/telemeta/collection_detail.html index 4b4cba10..78cee3ab 100644 --- a/telemeta/templates/telemeta/collection_detail.html +++ b/telemeta/templates/telemeta/collection_detail.html @@ -46,7 +46,10 @@ {% trans "Add item" %} {% endif %} {% if user.is_authenticated %} - {% trans "Add to playlist" %} + {% trans "Add to playlist" %} + {% if audio_export_enabled or perms.telemeta.can_download_all_items or user.is_superuser %} + {% trans "Download" %} ZIP + {% endif %} {% endif %} {% endblock %} diff --git a/telemeta/urls.py b/telemeta/urls.py index bb85197e..f91b992f 100644 --- a/telemeta/urls.py +++ b/telemeta/urls.py @@ -39,8 +39,8 @@ from django.conf import settings from django.views.generic.simple import redirect_to from telemeta.models import MediaItem, MediaCollection, MediaItemMarker, MediaCorpus, MediaFonds from telemeta.views import HomeView, AdminView, CollectionView, ItemView, \ - InstrumentView, InstrumentAliasView, PlaylistView, ProfileView, GeoView, \ - LastestRevisionsFeed, ResourceView, UserRevisionsFeed + InstrumentView, InstrumentAliasView, PlaylistView, ProfileView, GeoView, \ + LastestRevisionsFeed, ResourceView, UserRevisionsFeed, CollectionPackageView from jsonrpc import jsonrpc_site import os.path import telemeta.config @@ -170,7 +170,8 @@ urlpatterns = patterns('', dict(all_collections_sound, paginate_by=20, template_name="telemeta/collection_list.html"), name="telemeta-collections-sound"), # FIXME: need all paths url(r'^collections/(?P[A-Za-z0-9._-s/]+)/$', redirect_to, {'url': '/archives/collections/%(path)s/', 'permanent': False}, name="telemeta-collection-redir"), - + url(r'^archives/collections/(?P[A-Za-z0-9._-]+)/package/$', CollectionPackageView.as_view(), + name="telemeta-collection-package"), # RESOURCES # Corpus list url(r'^archives/corpus/$', 'django.views.generic.list_detail.object_list', @@ -359,6 +360,10 @@ urlpatterns = patterns('', 'document_root': settings.TELEMETA_CACHE_DIR,}), url(r'^', include('jqchat.urls')), -) +) +if settings.DEBUG: + import debug_toolbar + urlpatterns += patterns('', + url(r'^__debug__/', include(debug_toolbar.urls)),) \ No newline at end of file diff --git a/telemeta/views/collection.py b/telemeta/views/collection.py index f2039d47..a0aa61cb 100644 --- a/telemeta/views/collection.py +++ b/telemeta/views/collection.py @@ -160,27 +160,78 @@ class CollectionView(object): return render(request, template, {'collection': collection, 'formset': formset,}) -class CollectionPackageView(DetailView): +class CollectionPackageView(View): model = MediaCollection - def render_to_reponse(self, context): + def get_object(self): + return MediaCollection.objects.get(public_id=self.kwargs['public_id']) + + def get_stream(self, request, *args, **kwargs): + """ + Stream a ZIP file of collection data + without loading the whole file into memory. + Based on ZipStream + """ + from telemeta.views import MarkerView + from telemeta.backup import CollectionSerializer + import json + import zipstream + + z = zipstream.ZipFile() + collection = MediaCollection.objects.get(public_id=public_id) + z.write(collection.code) + + for item in collection.items.all(): + z.write(item.file.path) + + try: + from django.http import StreamingHttpResponse + response = StreamingHttpResponse(z, content_type='application/zip') + except: + response = HttpResponse(z, content_type='application/zip') + + response['Content-Disposition'] = "attachment; filename=%s.%s" % \ + (item.code, 'zip') + return response + + @method_decorator(login_required) + def dispatch(self, *args, **kwargs): + return super(CollectionPackageView, self).dispatch(*args, **kwargs) + + + """ Create a ZIP file on disk and transmit it in chunks of 8KB, without loading the whole file into memory. A similar approach can be used for large dynamic PDF files. """ + collection = self.get_object() - temp = tempfile.TemporaryFile() - archive = zipfile.ZipFile(temp, 'w', zipfile.ZIP_DEFLATED) + tmp_dir = getattr(settings, "FILE_UPLOAD_TEMP_DIR") + if not tmp_dir: + tmp_dir = '/tmp' + temp = tempfile.TemporaryFile(prefix=tmp_dir+os.sep) + archive = zipfile.ZipFile(temp, 'w', zipfile.ZIP_DEFLATED, allowZip64=True) + serializer = CollectionSerializer(collection) + archive.writestr('%s/%s%s' % (collection.code, collection.code, '.xml'), + serializer.get_xml().encode("utf-8")) + for item in collection.items.all(): - ext = item.file.path.splitext()[1] - archive.write(item.file, '%s.%s' % (code, ext)) + if item.file: + ext = os.path.splitext(item.file.path)[1] + archive.write(item.file.path, '%s/%s%s' % (collection.code, item.code, ext)) + marker_view = MarkerView() + markers = marker_view.get_markers(item.id) + if markers: + archive.writestr('%s/%s%s' % (collection.code, item.code, '.json'), json.dumps(markers)) + archive.close() - wrapper = FileWrapper(temp) + wrapper = FixedFileWrapper(temp) response = HttpResponse(wrapper, content_type='application/zip') + response['Content-Disposition'] = "attachment; filename=%s.%s" % \ - (item.code, 'zip') + (collection.code, 'zip') response['Content-Length'] = temp.tell() temp.seek(0) return response diff --git a/telemeta/views/core.py b/telemeta/views/core.py index 5cc102d1..22d7d03c 100644 --- a/telemeta/views/core.py +++ b/telemeta/views/core.py @@ -55,7 +55,7 @@ from django.http import HttpResponse, HttpResponseRedirect from django.http import Http404 from django.shortcuts import render_to_response, redirect, get_object_or_404 from django.views.generic import list_detail -from django.views.generic import DetailView +from django.views.generic import * from django.conf import settings from django.contrib import auth from django.contrib import messages -- 2.39.5