Customized pdf basically works.
authorMarcin Koziej <marcin.koziej@nowoczesnapolska.org.pl>
Thu, 1 Dec 2011 15:56:04 +0000 (16:56 +0100)
committerMarcin Koziej <marcin.koziej@nowoczesnapolska.org.pl>
Thu, 1 Dec 2011 15:56:04 +0000 (16:56 +0100)
apps/catalogue/forms.py
apps/catalogue/models.py
apps/catalogue/tests/book_import.py
apps/catalogue/urls.py
apps/catalogue/utils.py
apps/catalogue/views.py
wolnelektury/static/css/master.css
wolnelektury/static/js/catalogue.js
wolnelektury/templates/catalogue/book_detail.html

index 391e3e4..094aaaf 100644 (file)
@@ -96,3 +96,52 @@ class DownloadFormatsForm(forms.Form):
     def __init__(self, *args, **kwargs):
          super(DownloadFormatsForm, self).__init__(*args, **kwargs)
 
+
+PDF_PAGE_SIZES = (
+    ('a4paper', _('A4')),
+    ('a5paper', _('A5')),
+)
+
+
+PDF_LEADINGS = (
+    ('', _('Normal leading')),
+    ('onehalfleading', _('One and a half leading')),
+    ('doubleleading', _('Double leading')),
+    )
+
+PDF_FONT_SIZES = (
+    ('11pt', _('Default')),
+    ('13pt', _('Big'))
+    )
+
+
+class CustomPDFForm(forms.Form):
+    nofootnotes = forms.BooleanField(required=False, label=_("Don't show footnotes"))
+    nothemes = forms.BooleanField(required=False, label=_("Don't disply themes"))
+    nowlfont = forms.BooleanField(required=False, label=_("Don't use our custom font"))
+    ##    pagesize = forms.ChoiceField(PDF_PAGE_SIZES, required=True, label=_("Paper size"))
+    leading = forms.ChoiceField(PDF_LEADINGS, required=False, label=_("Leading"))
+    fontsize = forms.ChoiceField(PDF_FONT_SIZES, required=True, label=_("Font size"))
+
+    @property
+    def customizations(self):
+        c = []
+        if self.cleaned_data['nofootnotes']:
+            c.append('nofootnotes')
+            
+        if self.cleaned_data['nothemes']:
+            c.append('nothemes')
+            
+        if self.cleaned_data['nowlfont']:
+            c.append('nowlfont')
+        
+            ##  c.append(self.cleaned_data['pagesize'])
+        c.append(self.cleaned_data['fontsize'])
+
+        if self.cleaned_data['leading']:
+            c.append(self.cleaned_data['leading'])
+
+        c.sort()
+
+        return c
+
index c420119..2e20717 100644 (file)
@@ -23,6 +23,9 @@ from newtagging.models import TagBase, tags_updated
 from newtagging import managers
 from catalogue.fields import JSONField, OverwritingFileField
 from catalogue.utils import create_zip
+from shutil import copy
+
+from os import path
 
 
 TAG_CATEGORIES = (
@@ -170,23 +173,25 @@ class Tag(TagBase):
         return '/'.join((Tag.categories_dict[self.category], self.slug))
 
 
+def get_dynamic_path(media, filename, ext=None, maxlen=100):
+    from slughifi import slughifi
+    
+    # how to put related book's slug here?
+    if not ext:
+        if media.type == 'daisy':
+            ext = 'daisy.zip'
+        else:
+            ext = media.type
+    if media is None or not media.name:
+        name = slughifi(filename.split(".")[0])
+    else:
+        name = slughifi(media.name)
+    return 'book/%s/%s.%s' % (ext, name[:maxlen-len('book/%s/.%s' % (ext, ext))-4], ext)
+
+
 # TODO: why is this hard-coded ?
 def book_upload_path(ext=None, maxlen=100):
-    def get_dynamic_path(media, filename, ext=ext):
-        from slughifi import slughifi
-
-        # how to put related book's slug here?
-        if not ext:
-            if media.type == 'daisy':
-                ext = 'daisy.zip'
-            else:
-                ext = media.type
-        if not media.name:
-            name = slughifi(filename.split(".")[0])
-        else:
-            name = slughifi(media.name)
-        return 'book/%s/%s.%s' % (ext, name[:maxlen-len('book/%s/.%s' % (ext, ext))-4], ext)
-    return get_dynamic_path
+    return lambda *args: get_dynamic_path(*args, ext=ext, maxlen=maxlen)
 
 
 class BookMedia(models.Model):
@@ -465,24 +470,30 @@ class Book(models.Model):
     has_daisy_file.short_description = 'DAISY'
     has_daisy_file.boolean = True
 
-    def build_pdf(self):
+    def build_pdf(self, customizations=None, file_name=None):
         """ (Re)builds the pdf file.
-
+        customizations - customizations which are passed to LaTeX class file.
+        file_name - save the pdf file under a different name and DO NOT save it in db.
         """
         from tempfile import NamedTemporaryFile
         from os import unlink
         from django.core.files import File
         from librarian import pdf
         from catalogue.utils import ORMDocProvider, remove_zip
+        from django.core.files.move import file_move_safe
 
         try:
             pdf_file = NamedTemporaryFile(delete=False)
             pdf.transform(ORMDocProvider(self),
                       file_path=str(self.xml_file.path),
                       output_file=pdf_file,
+                      customizations=customizations
                       )
 
-            self.pdf_file.save('%s.pdf' % self.slug, File(open(pdf_file.name)))
+            if file_name is None:
+                self.pdf_file.save('%s.pdf' % self.slug, File(open(pdf_file.name)))
+            else:
+                copy(pdf_file.name, path.join(settings.MEDIA_ROOT, get_dynamic_path(None, file_name, ext='pdf')))
         finally:
             unlink(pdf_file.name)
 
index f65d880..f6110d0 100644 (file)
@@ -7,7 +7,7 @@ from catalogue import models
 
 from nose.tools import raises
 import tempfile
-from os import unlink,path
+from os import unlink, path, makedirs
 
 class BookImportLogicTests(WLTestCase):
 
@@ -258,3 +258,13 @@ class BookImportGenerateTest(WLTestCase):
         parent = models.Book.from_xml_file(input)
         parent.build_pdf()
         self.assertTrue(path.exists(parent.pdf_file.path))
+
+    def test_custom_pdf(self):
+        out = models.get_dynamic_path(None, 'test-custom', ext='pdf')
+        absoulute_path = path.join(settings.MEDIA_ROOT, out)
+        
+        if not path.exists(path.dirname(absoulute_path)):
+            makedirs(path.dirname(absoulute_path))
+
+        self.book.build_pdf(customizations=['nofootnotes', '13pt', 'a4paper'], file_name='test-custom')
+        self.assertTrue(path.exists(absoulute_path))
index c770892..324217a 100644 (file)
@@ -40,5 +40,7 @@ urlpatterns = patterns('catalogue.views',
     url(r'^(?P<tags>[a-zA-Z0-9-/]*)/$', 'tagged_object_list', name='tagged_object_list'),
 
     url(r'^audiobooki/(?P<type>mp3|ogg|daisy|all).xml$', AudiobookFeed(), name='audiobook_feed'),
+
+    url(r'^custompdf/(?P<slug>[a-zA-Z0-9-]+).pdf', 'download_custom_pdf'),
 )
 
index 0134701..85a9dc2 100644 (file)
@@ -8,6 +8,7 @@ import random
 import time
 from base64 import urlsafe_b64encode
 
+from django.http import HttpResponse, HttpResponseRedirect, Http404, HttpResponsePermanentRedirect
 from django.core.files.uploadedfile import UploadedFile
 from django.utils.hashcompat import sha_constructor
 from django.conf import settings
@@ -18,7 +19,9 @@ from fcntl import flock, LOCK_EX
 from zipfile import ZipFile
 
 from librarian import DocProvider
-
+from reporting.utils import read_chunks
+from celery.task import task
+import catalogue.models
 
 # Use the system (hardware-based) random number generator if it exists.
 if hasattr(random, 'SystemRandom'):
@@ -131,3 +134,23 @@ def remove_zip(zip_slug):
     except OSError as oe:
         if oe.errno != ENOENT:
             raise oe
+
+
+class AttachmentHttpResponse(HttpResponse):
+    """Response serving a file to be downloaded.
+    """
+    def __init__ (self, file_path, file_name, mimetype):
+        super(AttachmentHttpResponse, self).__init__(mimetype=mimetype)
+        self['Content-Disposition'] = 'attachment; filename=%s' % file_name
+        self.file_path = file_path
+        self.file_name = file_name
+
+        with open(self.file_path) as f:
+            for chunk in read_chunks(f):
+                self.write(chunk)
+
+@task
+def create_custom_pdf(book_id, customizations, file_name):
+    book = catalogue.models.Book.objects.get(id=book_id)
+    if not path.exists(file_name):
+        book.build_pdf(customizations=customizations, file_name=file_name)
index c808534..8169ad7 100644 (file)
@@ -28,11 +28,12 @@ from django.views.generic.list_detail import object_list
 
 from catalogue import models
 from catalogue import forms
-from catalogue.utils import split_tags
+from catalogue.utils import split_tags, AttachmentHttpResponse, create_custom_pdf
 from pdcounter import models as pdcounter_models
 from pdcounter import views as pdcounter_views
 from suggest.forms import PublishingSuggestForm
 
+from os import path
 
 staff_required = user_passes_test(lambda user: user.is_staff)
 
@@ -253,6 +254,7 @@ def book_detail(request, slug):
     projects = sorted(projects)
 
     form = forms.SearchForm()
+    custom_pdf_form = forms.CustomPDFForm()
     return render_to_response('catalogue/book_detail.html', locals(),
         context_instance=RequestContext(request))
 
@@ -762,3 +764,23 @@ def download_zip(request, format, slug):
     else:
         raise Http404('No format specified for zip package')
     return HttpResponseRedirect(urlquote_plus(settings.MEDIA_URL + url, safe='/?='))
+
+
+def download_custom_pdf(request, slug):
+    book = models.Book.objects.get(slug=slug)
+    if request.method == 'GET':
+        form = forms.CustomPDFForm(request.GET)
+        if form.is_valid():
+            cust = form.customizations
+            h = hash(tuple(cust))
+            pdf_name = '%s-custom-%s' % (book.slug, h)
+            pdf_file = path.join(settings.MEDIA_ROOT, models.get_dynamic_path(None, pdf_name, ext='pdf'))
+
+            if not path.exists(pdf_file):
+                result = create_custom_pdf.delay(book.id, cust, pdf_name)
+                result.wait()
+            return AttachmentHttpResponse(file_name=("%s.pdf" % book.slug), file_path=pdf_file, mimetype="application/pdf")
+        else:
+            raise Http404(_('Incorrect customization options for PDF'))
+    else:
+        raise Http404(_('Bad method'))
index e788e55..739ecee 100644 (file)
@@ -413,6 +413,11 @@ p .ac_input {
     padding: 0 10px 0 10px;    
 }
 
+#formats .wrap div.download .custom-pdf {
+    text-align: left;
+}
+
+
 #czytamysluchajac {
     margin-top: 2.5em;
 }
index ee8a045..e49f6b6 100644 (file)
@@ -583,7 +583,10 @@ function serverTime() {
                 }
             }); 
         }*/       
-
+       $("#custom-pdf-link").toggle(
+           function(ev) { $(".custom-pdf").show(); return false; },
+           function(ev) { $(".custom-pdf").hide(); return false; }
+       );
     });
 })(jQuery)
 
index ff7b519..5708b91 100644 (file)
                     {% for media in book.get_odt %}
                         <a href="{{ media.file.url }}"><img src="{{ STATIC_URL }}img/odt.png" title="{% trans "Download ODT" %} &ndash; {% trans "for reading" %} {% trans "and editing using" %} OpenOffice.org: {{ media.name }}" alt="{% trans "Download ODT" %}" /></a>
                     {% endfor %}
+                       
+                    {% if book.pdf_file %}
+                       <br/><a href="#" id="custom-pdf-link">{% trans "Dowload customized PDF" %}</a>.
+                   {% endif %}
+                       <div style="display: none" class="custom-pdf">
+                         <form action="{% url catalogue.views.download_custom_pdf book.slug %}" method="GET">
+                           {{custom_pdf_form.as_p}}
+                           <input type="submit" value="{% trans "Download" %}"/>
+                         </form>
+                       </div>
                 </div>
                 {% if book.has_mp3_file or book.has_ogg_file or book.has_daisy_file %}
                     <p class="header">