no fb2 on empty parents,
[wolnelektury.git] / apps / catalogue / models / book.py
index 4b50fc9..5672853 100644 (file)
@@ -160,20 +160,6 @@ class Book(models.Model):
                 provider=ORMDocProvider(self),
                 parse_dublincore=parse_dublincore)
 
-    def build_cover(self, book_info=None):
-        """(Re)builds the cover image."""
-        from StringIO import StringIO
-        from django.core.files.base import ContentFile
-        from librarian.cover import WLCover
-
-        if book_info is None:
-            book_info = self.wldocument().book_info
-
-        cover = WLCover(book_info).image()
-        imgstr = StringIO()
-        cover.save(imgstr, 'png')
-        self.cover.save(None, ContentFile(imgstr.getvalue()))
-
     def build_html(self):
         from django.core.files.base import ContentFile
         from slughifi import slughifi
@@ -233,6 +219,9 @@ class Book(models.Model):
         return False
 
     # Thin wrappers for builder tasks
+    def build_cover(self):
+        """(Re)builds the cover image."""
+        return tasks.build_cover.delay(self.pk)
     def build_pdf(self, *args, **kwargs):
         """(Re)builds PDF."""
         return tasks.build_pdf.delay(self.pk, *args, **kwargs)
@@ -315,7 +304,6 @@ class Book(models.Model):
                     raise Book.DoesNotExist(_('Book "%s" does not exist.') %
                             part_url.slug)
 
-
         # Read book metadata
         book_slug = book_info.url.slug
         if re.search(r'[^a-z0-9-]', book_slug):
@@ -344,24 +332,35 @@ class Book(models.Model):
 
         book.tags = set(meta_tags + book_shelves)
 
-        book_tag = book.book_tag()
-
+        obsolete_children = set(b for b in book.children.all() if b not in children)
         for n, child_book in enumerate(children):
             child_book.parent = book
             child_book.parent_number = n
             child_book.save()
-
-        # Save XML and HTML files
+        # Disown unfaithful children and let them cope on their own.
+        for child in obsolete_children:
+            child.parent = None
+            child.parent_number = 0
+            child.save()
+            tasks.fix_tree_tags.delay(child)
+
+        # Save XML file
         book.xml_file.save('%s.xml' % book.slug, raw_file, save=False)
 
         # delete old fragments when overwriting
         book.fragments.all().delete()
+        # Build HTML, fix the tree tags, build cover.
+        has_own_text = bool(book.build_html())
+        tasks.fix_tree_tags.delay(book)
+        book.build_cover(book_info)
+        
+        # No saves behind this point.
 
-        if book.build_html():
+        if has_own_text:
             if not settings.NO_BUILD_TXT and build_txt:
                 book.build_txt()
-
-        book.build_cover(book_info)
+            if not settings.NO_BUILD_FB2 and build_fb2:
+                book.build_fb2()
 
         if not settings.NO_BUILD_EPUB and build_epub:
             book.build_epub()
@@ -372,36 +371,49 @@ class Book(models.Model):
         if not settings.NO_BUILD_MOBI and build_mobi:
             book.build_mobi()
 
-        if not settings.NO_BUILD_FB2 and build_fb2:
-            book.build_fb2()
-
         if not settings.NO_SEARCH_INDEX and search_index:
             book.search_index(index_tags=search_index_tags, reuse_index=search_index_reuse)
             #index_book.delay(book.id, book_info)
 
-        book_descendants = list(book.children.all())
-        descendants_tags = set()
-        # add l-tag to descendants and their fragments
-        while len(book_descendants) > 0:
-            child_book = book_descendants.pop(0)
-            descendants_tags.update(child_book.tags)
-            child_book.tags = list(child_book.tags) + [book_tag]
-            child_book.save()
-            for fragment in child_book.fragments.all().iterator():
-                fragment.tags = set(list(fragment.tags) + [book_tag])
-            book_descendants += list(child_book.children.all())
-
-        for tag in descendants_tags:
-            tasks.touch_tag(tag)
+        cls.published.send(sender=book)
+        return book
 
-        book.save()
+    def fix_tree_tags(self):
+        """Fixes the l-tags on the book's subtree.
 
-        # refresh cache
-        book.reset_tag_counter()
-        book.reset_theme_counter()
+        Makes sure that:
+        * the book has its parents book-tags,
+        * its fragments have the book's and its parents book-tags,
+        * runs those for every child book too,
+        * touches all relevant tags,
+        * resets tag and theme counter on the book and its ancestry.
+        """
+        def fix_subtree(book, parent_tags):
+            affected_tags = set(book.tags)
+            book.tags = list(book.tags.exclude(category='book')) + parent_tags
+            sub_parent_tags = parent_tags + [book.book_tag()]
+            for frag in book.fragments.all():
+                affected_tags.update(frag.tags)
+                frag.tags = list(frag.tags.exclude(category='book')) + sub_parent_tags
+            for child in book.children.all():
+                affected_tags.update(fix_subtree(child, sub_parent_tags))
+            return affected_tags
+
+        parent_tags = []
+        parent = self.parent
+        while parent is not None:
+            parent_tags.append(parent.book_tag())
+            parent = parent.parent
+
+        affected_tags = fix_subtree(self, parent_tags)
+        for tag in affected_tags:
+            tasks.touch_tag(tag)
 
-        cls.published.send(sender=book)
-        return book
+        book = self
+        while book is not None:
+            book.reset_tag_counter()
+            book.reset_theme_counter()
+            book = book.parent
 
     def related_info(self):
         """Keeps info about related objects (tags, media) in cache field."""