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
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
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':
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")
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)
)
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.
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
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."
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
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: