Ebooks are now named after the book's slug and overwrite old versions.
[wolnelektury.git] / apps / catalogue / models / book.py
index 02051fb..414d1ef 100644 (file)
@@ -3,7 +3,7 @@
 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
 #
 import re
 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
 #
 import re
-from django.conf import settings as settings
+from django.conf import settings
 from django.core.cache import get_cache
 from django.db import models
 from django.db.models import permalink
 from django.core.cache import get_cache
 from django.db import models
 from django.db.models import permalink
@@ -11,14 +11,16 @@ import django.dispatch
 from django.utils.datastructures import SortedDict
 from django.utils.translation import ugettext_lazy as _
 import jsonfield
 from django.utils.datastructures import SortedDict
 from django.utils.translation import ugettext_lazy as _
 import jsonfield
+from fnpdjango.storage import BofhFileSystemStorage
 from catalogue import constants
 from catalogue.fields import EbookField
 from catalogue.models import Tag, Fragment, BookMedia
 from catalogue import constants
 from catalogue.fields import EbookField
 from catalogue.models import Tag, Fragment, BookMedia
-from catalogue.utils import create_zip, split_tags, book_upload_path
+from catalogue.utils import create_zip, split_tags, related_tag_name
 from catalogue import app_settings
 from catalogue import tasks
 from newtagging import managers
 
 from catalogue import app_settings
 from catalogue import tasks
 from newtagging import managers
 
+bofh_storage = BofhFileSystemStorage()
 
 permanent_cache = get_cache('permanent')
 
 
 permanent_cache = get_cache('permanent')
 
@@ -27,6 +29,7 @@ class Book(models.Model):
     """Represents a book imported from WL-XML."""
     title         = models.CharField(_('title'), max_length=120)
     sort_key = models.CharField(_('sort key'), max_length=120, db_index=True, editable=False)
     """Represents a book imported from WL-XML."""
     title         = models.CharField(_('title'), max_length=120)
     sort_key = models.CharField(_('sort key'), max_length=120, db_index=True, editable=False)
+    sort_key_author = models.CharField(_('sort key by author'), max_length=120, db_index=True, editable=False, default=u'')
     slug = models.SlugField(_('slug'), max_length=120, db_index=True,
             unique=True)
     common_slug = models.SlugField(_('slug'), max_length=120, db_index=True)
     slug = models.SlugField(_('slug'), max_length=120, db_index=True,
             unique=True)
     common_slug = models.SlugField(_('slug'), max_length=120, db_index=True)
@@ -42,7 +45,14 @@ class Book(models.Model):
     # files generated during publication
 
     cover = EbookField('cover', _('cover'),
     # files generated during publication
 
     cover = EbookField('cover', _('cover'),
-                upload_to=book_upload_path('jpg'), null=True, blank=True)
+            null=True, blank=True,
+            upload_to=lambda i, n: 'book/cover/%s.jpg' % i.slug,
+            storage=bofh_storage, max_length=255)
+    # Cleaner version of cover for thumbs
+    cover_thumb = EbookField('cover_thumb', _('cover thumbnail'), 
+            null=True, blank=True,
+            upload_to=lambda i, n: 'book/cover_thumb/%s.jpg' % i.slug,
+            max_length=255)
     ebook_formats = constants.EBOOK_FORMATS
     formats = ebook_formats + ['html', 'xml']
 
     ebook_formats = constants.EBOOK_FORMATS
     formats = ebook_formats + ['html', 'xml']
 
@@ -74,6 +84,7 @@ class Book(models.Model):
         from sortify import sortify
 
         self.sort_key = sortify(self.title)
         from sortify import sortify
 
         self.sort_key = sortify(self.title)
+        self.title = unicode(self.title) # ???
 
         ret = super(Book, self).save(force_insert, force_update, **kwargs)
 
 
         ret = super(Book, self).save(force_insert, force_update, **kwargs)
 
@@ -95,6 +106,12 @@ class Book(models.Model):
     def name(self):
         return self.title
 
     def name(self):
         return self.title
 
+    def language_code(self):
+        return constants.LANGUAGES_3TO2.get(self.language, self.language)
+
+    def language_name(self):
+        return dict(settings.LANGUAGES).get(self.language_code(), "")
+
     def book_tag_slug(self):
         return ('l-' + self.slug)[:120]
 
     def book_tag_slug(self):
         return ('l-' + self.slug)[:120]
 
@@ -117,7 +134,7 @@ class Book(models.Model):
         if self.has_media(type_):
             if type_ in Book.formats:
                 return getattr(self, "%s_file" % 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_)
         else:
             return None
                 return self.media.filter(type=type_)
         else:
             return None
@@ -129,7 +146,7 @@ class Book(models.Model):
     def get_ogg(self):
         return self.get_media("ogg")
     def get_daisy(self):
     def get_ogg(self):
         return self.get_media("ogg")
     def get_daisy(self):
-        return self.get_media("daisy")                       
+        return self.get_media("daisy")
 
     def reset_short_html(self):
         if self.id is None:
 
     def reset_short_html(self):
         if self.id is None:
@@ -140,6 +157,14 @@ class Book(models.Model):
         for fragm in self.fragments.all().iterator():
             fragm.reset_short_html()
 
         for fragm in self.fragments.all().iterator():
             fragm.reset_short_html()
 
+        try:
+            author = self.tags.filter(category='author')[0].sort_key
+        except IndexError:
+            author = u''
+        type(self).objects.filter(pk=self.pk).update(sort_key_author=author)
+
+
+
     def has_description(self):
         return len(self.description) > 0
     has_description.short_description = _('description')
     def has_description(self):
         return len(self.description) > 0
     has_description.short_description = _('description')
@@ -303,7 +328,8 @@ class Book(models.Model):
         tasks.fix_tree_tags.delay(book)
         if 'cover' not in dont_build:
             book.cover.build_delay()
         tasks.fix_tree_tags.delay(book)
         if 'cover' not in dont_build:
             book.cover.build_delay()
-        
+            book.cover_thumb.build_delay()
+
         # No saves behind this point.
 
         if has_own_text:
         # No saves behind this point.
 
         if has_own_text:
@@ -385,12 +411,17 @@ class Book(models.Model):
         if not self.cover_info(inherit=False):
             if 'cover' not in app_settings.DONT_BUILD:
                 self.cover.build_delay()
         if not self.cover_info(inherit=False):
             if 'cover' not in app_settings.DONT_BUILD:
                 self.cover.build_delay()
+                self.cover_thumb.build_delay()
             for format_ in constants.EBOOK_FORMATS_WITH_COVERS:
                 if format_ not in app_settings.DONT_BUILD:
                     getattr(self, '%s_file' % format_).build_delay()
             for child in self.children.all():
                 child.parent_cover_changed()
 
             for format_ in constants.EBOOK_FORMATS_WITH_COVERS:
                 if format_ not in app_settings.DONT_BUILD:
                     getattr(self, '%s_file' % format_).build_delay()
             for child in self.children.all():
                 child.parent_cover_changed()
 
+    def other_versions(self):
+        """Find other versions (i.e. in other languages) of the book."""
+        return type(self).objects.filter(common_slug=self.common_slug).exclude(pk=self.pk)
+
     def related_info(self):
         """Keeps info about related objects (tags, media) in cache field."""
         if self._related_info is not None:
     def related_info(self):
         """Keeps info about related objects (tags, media) in cache field."""
         if self._related_info is not None:
@@ -402,8 +433,15 @@ class Book(models.Model):
                     'author', 'kind', 'genre', 'epoch'))
             tags = split_tags(tags)
             for category in tags:
                     'author', 'kind', 'genre', 'epoch'))
             tags = split_tags(tags)
             for category in tags:
-                rel['tags'][category] = [
-                        (t.name, t.slug) for t in tags[category]]
+                cat = []
+                for tag in tags[category]:
+                    tag_info = {'slug': tag.slug, 'name': tag.name}
+                    for lc, ln in settings.LANGUAGES:
+                        tag_name = getattr(tag, "name_%s" % lc)
+                        if tag_name:
+                            tag_info["name_%s" % lc] = tag_name
+                    cat.append(tag_info)
+                rel['tags'][category] = cat
 
             for media_format in BookMedia.formats:
                 rel['media'][media_format] = self.has_media(media_format)
 
             for media_format in BookMedia.formats:
                 rel['media'][media_format] = self.has_media(media_format)
@@ -487,8 +525,8 @@ class Book(models.Model):
     def pretty_title(self, html_links=False):
         book = self
         rel_info = book.related_info()
     def pretty_title(self, html_links=False):
         book = self
         rel_info = book.related_info()
-        names = [(name, Tag.create_url('author', slug))
-                    for name, slug in rel_info['tags']['author']]
+        names = [(related_tag_name(tag), Tag.create_url('author', tag['slug']))
+                    for tag in rel_info['tags'].get('author', ())]
         if 'parents' in rel_info:
             books = [(name, Book.create_url(slug))
                         for name, slug in rel_info['parents']]
         if 'parents' in rel_info:
             books = [(name, Book.create_url(slug))
                         for name, slug in rel_info['parents']]
@@ -511,7 +549,7 @@ class Book(models.Model):
         """
         # get relevant books and their tags
         objects = cls.tagged.with_all(tags)
         """
         # get relevant books and their tags
         objects = cls.tagged.with_all(tags)
-        parents = objects.filter(html_file='').only('slug')
+        parents = objects.exclude(children=None).only('slug')
         # eliminate descendants
         l_tags = Tag.objects.filter(category='book',
             slug__in=[book.book_tag_slug() for book in parents.iterator()])
         # eliminate descendants
         l_tags = Tag.objects.filter(category='book',
             slug__in=[book.book_tag_slug() for book in parents.iterator()])
@@ -534,7 +572,7 @@ class Book(models.Model):
                 'title', 'parent', 'slug')
         if filter:
             books = books.filter(filter).distinct()
                 'title', 'parent', 'slug')
         if filter:
             books = books.filter(filter).distinct()
-            
+
             book_ids = set(b['pk'] for b in books.values("pk").iterator())
             for book in books.iterator():
                 parent = book.parent_id
             book_ids = set(b['pk'] for b in books.values("pk").iterator())
             for book in books.iterator():
                 parent = book.parent_id
@@ -550,7 +588,7 @@ class Book(models.Model):
         for tag in Tag.objects.filter(category='author').iterator():
             books_by_author[tag] = []
 
         for tag in Tag.objects.filter(category='author').iterator():
             books_by_author[tag] = []
 
-        for book in books_by_parent.get(None,()):
+        for book in books_by_parent.get(None, ()):
             authors = list(book.tags.filter(category='author'))
             if authors:
                 for author in authors:
             authors = list(book.tags.filter(category='author'))
             if authors:
                 for author in authors:
@@ -588,6 +626,13 @@ class Book(models.Model):
 # add the file fields
 for format_ in Book.formats:
     field_name = "%s_file" % format_
 # add the file fields
 for format_ in Book.formats:
     field_name = "%s_file" % format_
+    upload_to = (lambda upload_path:
+            lambda i, n: upload_path % i.slug
+        )('book/%s/%%s.%s' % (format_, format_))
     EbookField(format_, _("%s file" % format_.upper()),
     EbookField(format_, _("%s file" % format_.upper()),
-            upload_to=book_upload_path(format_),
-            blank=True, default='').contribute_to_class(Book, field_name)
+        upload_to=upload_to,
+        storage=bofh_storage,
+        max_length=255,
+        blank=True,
+        default=''
+    ).contribute_to_class(Book, field_name)