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
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
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
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
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
# 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 \
@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()
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'):
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.
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
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)