Merge branch 'master' of github.com:fnp/wolnelektury
[wolnelektury.git] / apps / catalogue / models.py
index 556a8a1..ba1a5d2 100644 (file)
@@ -5,10 +5,9 @@
 from collections import namedtuple
 
 from django.db import models
 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
 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.translation import ugettext_lazy as _
 from django.contrib.auth.models import User
 from django.template.loader import render_to_string
@@ -16,7 +15,7 @@ from django.utils.datastructures import SortedDict
 from django.utils.safestring import mark_safe
 from django.utils.translation import get_language
 from django.core.urlresolvers import reverse
 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
 import jsonfield
 
 from django.conf import settings
@@ -25,11 +24,8 @@ from newtagging.models import TagBase, tags_updated
 from newtagging import managers
 from catalogue.fields import JSONField, OverwritingFileField
 from catalogue.utils import create_zip, split_tags, truncate_html_words
 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
 import re
-from os import path
 
 import search
 
 
 import search
 
@@ -216,32 +212,6 @@ def book_upload_path(ext=None, maxlen=100):
     return lambda *args: get_dynamic_path(*args, ext=ext, maxlen=maxlen)
 
 
     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([
 class BookMedia(models.Model):
     FileFormat = namedtuple("FileFormat", "name ext")
     formats = SortedDict([
@@ -274,7 +244,7 @@ class BookMedia(models.Model):
 
         try:
             old = BookMedia.objects.get(pk=self.pk)
 
         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
             old = None
         else:
             # if name changed, change the file name, too
@@ -428,18 +398,18 @@ class Book(models.Model):
             book_tag.save()
         return book_tag
 
             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:
         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:                                             
             else:                                             
-                return self.media.filter(type=type)
+                return self.media.filter(type=type_)
         else:
             return None
 
         else:
             return None
 
@@ -504,69 +474,6 @@ class Book(models.Model):
         cover.save(imgstr, 'png')
         self.cover.save(None, ContentFile(imgstr.getvalue()))
 
         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
     def build_html(self):
         from django.core.files.base import ContentFile
         from slughifi import slughifi
@@ -624,6 +531,16 @@ class Book(models.Model):
             return True
         return False
 
             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):
     @staticmethod
     def zip_format(format_):
         def pretty_file_name(book):
@@ -636,15 +553,13 @@ class Book(models.Model):
         books = Book.objects.filter(parent=None).exclude(**{field_name: ""})
         paths = [(pretty_file_name(b), getattr(b, field_name).path)
                     for b in books]
         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()))
                     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)
 
     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 search_index(self, book_info=None, reuse_index=False, index_tags=True):
         if reuse_index:
@@ -680,8 +595,6 @@ class Book(models.Model):
     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):
     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 = []
 
         # check for parts before we do anything
         children = []
@@ -689,7 +602,7 @@ class Book(models.Model):
             for part_url in book_info.parts:
                 try:
                     children.append(Book.objects.get(slug=part_url.slug))
             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)
 
                     raise Book.DoesNotExist(_('Book "%s" does not exist.') %
                             part_url.slug)
 
@@ -767,7 +680,7 @@ class Book(models.Model):
             book_descendants += list(child_book.children.all())
 
         for tag in descendants_tags:
             book_descendants += list(child_book.children.all())
 
         for tag in descendants_tags:
-            touch_tag(tag)
+            tasks.touch_tag(tag)
 
         book.save()
 
 
         book.save()
 
@@ -808,6 +721,13 @@ class Book(models.Model):
                 type(self).objects.filter(pk=self.pk).update(_related_info=rel)
             return rel
 
                 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
     def reset_tag_counter(self):
         if self.id is None:
             return
@@ -1055,7 +975,7 @@ def _tags_updated_handler(sender, affected_tags, **kwargs):
     # reset tag global counter
     # we want Tag.changed_at updated for API to know the tag was touched
     for tag in affected_tags:
     # 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 \
 
     # if book tags changed, reset book tag counter
     if isinstance(sender, Book) and \
@@ -1087,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."""
 @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 = search.Index()
     idx.open(timeout=10000)  # 10 seconds timeout.
     try:
         idx.remove_book(instance)
+        idx.index_tags()
     finally:
         idx.close()
     finally:
         idx.close()