customized pdf generation.
authorMarcin Koziej <marcin.koziej@nowoczesnapolska.org.pl>
Mon, 5 Dec 2011 12:58:40 +0000 (13:58 +0100)
committerMarcin Koziej <marcin.koziej@nowoczesnapolska.org.pl>
Mon, 5 Dec 2011 12:58:40 +0000 (13:58 +0100)
apps/catalogue/models.py
apps/catalogue/utils.py
apps/catalogue/views.py

index 2e20717..b82879b 100644 (file)
@@ -8,6 +8,7 @@ from django.db import models
 from django.db.models import permalink, Q
 import django.dispatch
 from django.core.cache import cache
+from django.core.files.storage import DefaultStorage
 from django.utils.translation import ugettext_lazy as _
 from django.contrib.auth.models import User
 from django.template.loader import render_to_string
@@ -24,7 +25,8 @@ from newtagging import managers
 from catalogue.fields import JSONField, OverwritingFileField
 from catalogue.utils import create_zip
 from shutil import copy
-
+from glob import glob
+import re
 from os import path
 
 
@@ -175,7 +177,7 @@ class Tag(TagBase):
 
 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':
@@ -194,6 +196,27 @@ def book_upload_path(ext=None, maxlen=100):
     return lambda *args: get_dynamic_path(*args, ext=ext, maxlen=maxlen)
 
 
+def get_customized_pdf_path(book, customizations):
+    """
+    Returns a MEDIA_ROOT relative path for a customized pdf. The name will contain a hash of customization options.
+    """
+    customizations.sort()
+    h = hash(tuple(customizations))
+    pdf_name = '%s-custom-%s' % (book.slug, h)
+    pdf_file = models.get_dynamic_path(None, pdf_name, ext='pdf')
+    return pdf_file
+
+
+def get_existing_customized_pdf(book):
+    """
+    Returns a list of paths to generated customized pdf of a book
+    """
+    pdf_glob = '%s-custom-' % (book.slug,)
+    pdf_glob = get_dynamic_path(None, pdf_glob, ext='pdf')
+    pdf_glob = re.sub(r"[.]([a-z0-9]+)$", "*.\\1", pdf_glob)
+    return glob(path.join(settings.MEDIA_ROOT, pdf_glob))
+
+
 class BookMedia(models.Model):
     type        = models.CharField(_('type'), choices=MEDIA_FORMATS, max_length="100")
     name        = models.CharField(_('name'), max_length="100")
@@ -480,7 +503,6 @@ class Book(models.Model):
         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)
@@ -491,14 +513,22 @@ class Book(models.Model):
                       )
 
             if file_name is None:
-                self.pdf_file.save('%s.pdf' % self.slug, File(open(pdf_file.name)))
+                # we'd like to be sure not to overwrite changes happening while
+                # (timely) pdf generation is taking place (async celery scenario)
+                current_self = Book.objects.get(id=self.id)
+                current_self.pdf_file.save('%s.pdf' % self.slug, File(open(pdf_file.name)))
+                self.pdf_file = current_self.pdf_file
             else:
-                copy(pdf_file.name, path.join(settings.MEDIA_ROOT, get_dynamic_path(None, file_name, ext='pdf')))
+                print "safing %s" % file_name
+                print "to: %s" % DefaultStorage().path(file_name)
+                DefaultStorage().save(file_name, File(open(pdf_file.name)))
         finally:
             unlink(pdf_file.name)
 
-        # remove zip with all pdf files
+        # remove cached downloadables
         remove_zip(settings.ALL_PDF_ZIP)
+        for customized_pdf in get_existing_customized_pdf(self):
+            unlink(customized_pdf)
 
     def build_mobi(self):
         """ (Re)builds the MOBI file.
index 85a9dc2..3ffe9c0 100644 (file)
@@ -10,6 +10,8 @@ from base64 import urlsafe_b64encode
 
 from django.http import HttpResponse, HttpResponseRedirect, Http404, HttpResponsePermanentRedirect
 from django.core.files.uploadedfile import UploadedFile
+from django.core.files.base import File
+from django.core.files.storage import DefaultStorage
 from django.utils.hashcompat import sha_constructor
 from django.conf import settings
 from celery.task import task
@@ -145,12 +147,19 @@ class AttachmentHttpResponse(HttpResponse):
         self.file_path = file_path
         self.file_name = file_name
 
-        with open(self.file_path) as f:
+        with open(DefaultStorage().path(self.file_path)) as f:
             for chunk in read_chunks(f):
                 self.write(chunk)
 
 @task
-def create_custom_pdf(book_id, customizations, file_name):
+def async_build_pdf(book_id, customizations, file_name):
+    """
+    A celery task to generate pdf files.
+    Accepts the same args as Book.build_pdf, but with book id as first parameter
+    instead of Book instance
+    """
     book = catalogue.models.Book.objects.get(id=book_id)
-    if not path.exists(file_name):
+    print "will gen %s" % DefaultStorage().path(file_name)
+    if not DefaultStorage().exists(file_name):
         book.build_pdf(customizations=customizations, file_name=file_name)
+    print "done."
index 8169ad7..2bc1f10 100644 (file)
@@ -28,7 +28,7 @@ from django.views.generic.list_detail import object_list
 
 from catalogue import models
 from catalogue import forms
-from catalogue.utils import split_tags, AttachmentHttpResponse, create_custom_pdf
+from catalogue.utils import split_tags, AttachmentHttpResponse, async_build_pdf
 from pdcounter import models as pdcounter_models
 from pdcounter import views as pdcounter_views
 from suggest.forms import PublishingSuggestForm
@@ -772,12 +772,10 @@ def download_custom_pdf(request, slug):
         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'))
-
+            pdf_file = models.get_customized_pdf_path(book, cust)
+                
             if not path.exists(pdf_file):
-                result = create_custom_pdf.delay(book.id, cust, pdf_name)
+                result = async_build_pdf.delay(book.id, cust, pdf_file)
                 result.wait()
             return AttachmentHttpResponse(file_name=("%s.pdf" % book.slug), file_path=pdf_file, mimetype="application/pdf")
         else: