Merge branch 'custompdf'
authorRadek Czajka <radoslaw.czajka@nowoczesnapolska.org.pl>
Thu, 29 Mar 2012 09:59:12 +0000 (11:59 +0200)
committerRadek Czajka <radoslaw.czajka@nowoczesnapolska.org.pl>
Thu, 29 Mar 2012 09:59:12 +0000 (11:59 +0200)
Conflicts:
wolnelektury/settings/__init__.py

1  2 
apps/catalogue/models.py
apps/catalogue/utils.py

diff --combined apps/catalogue/models.py
@@@ -5,10 -5,9 +5,9 @@@
  from collections import namedtuple
  
  from django.db import models
- from django.db.models import permalink, Q
+ from django.db.models import permalink
  import django.dispatch
  from django.core.cache import get_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
@@@ -16,7 -15,7 +15,7 @@@ from django.utils.datastructures impor
  from django.utils.safestring import mark_safe
  from django.utils.translation import get_language
  from django.core.urlresolvers import reverse
- from django.db.models.signals import post_save, m2m_changed, pre_delete, post_delete
+ from django.db.models.signals import post_save, pre_delete, post_delete
  import jsonfield
  
  from django.conf import settings
@@@ -25,11 -24,8 +24,8 @@@ from newtagging.models import TagBase, 
  from newtagging import managers
  from catalogue.fields import JSONField, OverwritingFileField
  from catalogue.utils import create_zip, split_tags, truncate_html_words
- from catalogue.tasks import touch_tag, index_book
- from shutil import copy
- from glob import glob
+ from catalogue import tasks
  import re
- from os import path
  
  import search
  
@@@ -216,32 -212,6 +212,6 @@@ def book_upload_path(ext=None, maxlen=1
      return lambda *args: get_dynamic_path(*args, ext=ext, maxlen=maxlen)
  
  
- def customizations_hash(customizations):
-     customizations.sort()
-     return hash(tuple(customizations))
- 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.
-     """
-     h = customizations_hash(customizations)
-     pdf_name = '%s-custom-%s' % (book.slug, h)
-     pdf_file = 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):
      FileFormat = namedtuple("FileFormat", "name ext")
      formats = SortedDict([
  
          try:
              old = BookMedia.objects.get(pk=self.pk)
-         except BookMedia.DoesNotExist, e:
+         except BookMedia.DoesNotExist:
              old = None
          else:
              # if name changed, change the file name, too
@@@ -428,18 -398,18 +398,18 @@@ class Book(models.Model)
              book_tag.save()
          return book_tag
  
-     def has_media(self, type):
-         if type in Book.formats:
-             return bool(getattr(self, "%s_file" % type))
+     def has_media(self, type_):
+         if type_ in Book.formats:
+             return bool(getattr(self, "%s_file" % type_))
          else:
-             return self.media.filter(type=type).exists()
+             return self.media.filter(type=type_).exists()
  
-     def get_media(self, type):
-         if self.has_media(type):
-             if type in Book.formats:
-                 return getattr(self, "%s_file" % type)
+     def get_media(self, type_):
+         if self.has_media(type_):
+             if type_ in Book.formats:
+                 return getattr(self, "%s_file" % type_)
              else:                                             
-                 return self.media.filter(type=type)
+                 return self.media.filter(type=type_)
          else:
              return None
  
          cover.save(imgstr, 'png')
          self.cover.save(None, ContentFile(imgstr.getvalue()))
  
-     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 os import unlink
-         from django.core.files import File
-         from catalogue.utils import remove_zip
-         pdf = self.wldocument().as_pdf(customizations=customizations)
-         if file_name is None:
-             # 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.get_filename())))
-             self.pdf_file = current_self.pdf_file
-             # remove cached downloadables
-             remove_zip(settings.ALL_PDF_ZIP)
-             for customized_pdf in get_existing_customized_pdf(self):
-                 unlink(customized_pdf)
-         else:
-             print "saving %s" % file_name
-             print "to: %s" % DefaultStorage().path(file_name)
-             DefaultStorage().save(file_name, File(open(pdf.get_filename())))
-     def build_mobi(self):
-         """ (Re)builds the MOBI file.
-         """
-         from django.core.files import File
-         from catalogue.utils import remove_zip
-         mobi = self.wldocument().as_mobi()
-         self.mobi_file.save('%s.mobi' % self.slug, File(open(mobi.get_filename())))
-         # remove zip with all mobi files
-         remove_zip(settings.ALL_MOBI_ZIP)
-     def build_epub(self):
-         """(Re)builds the epub file."""
-         from django.core.files import File
-         from catalogue.utils import remove_zip
-         epub = self.wldocument().as_epub()
-         self.epub_file.save('%s.epub' % self.slug,
-                 File(open(epub.get_filename())))
-         # remove zip package with all epub files
-         remove_zip(settings.ALL_EPUB_ZIP)
-     def build_txt(self):
-         from django.core.files.base import ContentFile
-         text = self.wldocument().as_text()
-         self.txt_file.save('%s.txt' % self.slug, ContentFile(text.get_string()))
      def build_html(self):
          from django.core.files.base import ContentFile
          from slughifi import slughifi
              return True
          return False
  
+     # Thin wrappers for builder tasks
+     def build_pdf(self, *args, **kwargs):
+         return tasks.build_pdf.delay(self.pk, *args, **kwargs)
+     def build_epub(self, *args, **kwargs):
+         return tasks.build_epub.delay(self.pk, *args, **kwargs)
+     def build_mobi(self, *args, **kwargs):
+         return tasks.build_mobi.delay(self.pk, *args, **kwargs)
+     def build_txt(self, *args, **kwargs):
+         return tasks.build_txt.delay(self.pk, *args, **kwargs)
      @staticmethod
      def zip_format(format_):
          def pretty_file_name(book):
          books = Book.objects.filter(parent=None).exclude(**{field_name: ""})
          paths = [(pretty_file_name(b), getattr(b, field_name).path)
                      for b in books]
-         result = create_zip.delay(paths,
+         return create_zip(paths,
                      getattr(settings, "ALL_%s_ZIP" % format_.upper()))
-         return result.wait()
  
      def zip_audiobooks(self, format_):
          bm = BookMedia.objects.filter(book=self, type=format_)
          paths = map(lambda bm: (None, bm.file.path), bm)
-         result = create_zip.delay(paths, "%s_%s" % (self.slug, format_))
-         return result.wait()
+         return create_zip(paths, "%s_%s" % (self.slug, format_))
  
      def search_index(self, book_info=None, reuse_index=False, index_tags=True):
          if reuse_index:
      def from_text_and_meta(cls, raw_file, book_info, overwrite=False,
              build_epub=True, build_txt=True, build_pdf=True, build_mobi=True,
              search_index=True, search_index_tags=True, search_index_reuse=False):
-         import re
-         from sortify import sortify
  
          # check for parts before we do anything
          children = []
              for part_url in book_info.parts:
                  try:
                      children.append(Book.objects.get(slug=part_url.slug))
-                 except Book.DoesNotExist, e:
+                 except Book.DoesNotExist:
                      raise Book.DoesNotExist(_('Book "%s" does not exist.') %
                              part_url.slug)
  
              book_descendants += list(child_book.children.all())
  
          for tag in descendants_tags:
-             touch_tag(tag)
+             tasks.touch_tag(tag)
  
          book.save()
  
                  type(self).objects.filter(pk=self.pk).update(_related_info=rel)
              return rel
  
+     def related_themes(self):
+         theme_counter = self.theme_counter
+         book_themes = Tag.objects.filter(pk__in=theme_counter.keys())
+         for tag in book_themes:
+             tag.count = theme_counter[tag.pk]
+         return book_themes
      def reset_tag_counter(self):
          if self.id is None:
              return
@@@ -1055,7 -975,7 +975,7 @@@ def _tags_updated_handler(sender, affec
      # reset tag global counter
      # we want Tag.changed_at updated for API to know the tag was touched
      for tag in affected_tags:
-         touch_tag(tag)
+         tasks.touch_tag(tag)
  
      # if book tags changed, reset book tag counter
      if isinstance(sender, Book) and \
@@@ -1087,11 -1007,9 +1007,11 @@@ post_save.connect(_post_save_handler
  @django.dispatch.receiver(post_delete, sender=Book)
  def _remove_book_from_index_handler(sender, instance, **kwargs):
      """ remove the book from search index, when it is deleted."""
 +    search.JVM.attachCurrentThread()
      idx = search.Index()
      idx.open(timeout=10000)  # 10 seconds timeout.
      try:
          idx.remove_book(instance)
 +        idx.index_tags()
      finally:
          idx.close()
diff --combined apps/catalogue/utils.py
@@@ -9,23 -9,18 +9,18 @@@ import r
  import time
  from base64 import urlsafe_b64encode
  
- from django.http import HttpResponse, HttpResponseRedirect, Http404, HttpResponsePermanentRedirect
+ from django.http import HttpResponse
  from django.core.files.uploadedfile import UploadedFile
- from django.core.files.base import File
  from django.core.files.storage import DefaultStorage
  from django.utils.encoding import force_unicode
  from django.utils.hashcompat import sha_constructor
  from django.conf import settings
- from celery.task import task
  from os import mkdir, path, unlink
  from errno import EEXIST, ENOENT
  from fcntl import flock, LOCK_EX
  from zipfile import ZipFile
- from traceback import print_exc
  
  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'):
@@@ -79,12 -74,12 +74,12 @@@ class LockFile(object)
          try:
              unlink(self.lockname)
          except OSError as oe:
 -            if oe.errno != oe.EEXIST:
 +            if oe.errno != EEXIST:
                  raise oe
          self.lock.close()
  
  
- @task
#@task
  def create_zip(paths, zip_slug):
      """
      Creates a zip in MEDIA_ROOT/zip directory containing files from path.
@@@ -140,25 -135,6 +135,6 @@@ class AttachmentHttpResponse(HttpRespon
              for chunk in read_chunks(f):
                  self.write(chunk)
  
- @task
- 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
-     """
-     try:
-         book = catalogue.models.Book.objects.get(id=book_id)
-         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."
-     except Exception, e:
-         print "Error during pdf creation: %s" % e
-         print_exc
-         raise e
  class MultiQuerySet(object):
      def __init__(self, *args, **kwargs):
          self.querysets = args
@@@ -260,3 -236,24 +236,24 @@@ def truncate_html_words(s, num, end_tex
          out += '</%s>' % tag
      # Return string
      return out
+ def customizations_hash(customizations):
+     customizations.sort()
+     return hash(tuple(customizations))
+ 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.
+     """
+     h = customizations_hash(customizations)
+     return 'book/%s/%s-custom-%s.pdf' % (book.slug, book.slug, h)
+ def clear_custom_pdf(book):
+     """
+     Returns a list of paths to generated customized pdf of a book
+     """
+     from waiter.utils import clear_cache
+     clear_cache('book/%s' % book.slug)