Merge commit '645219bfa68384dad930269b94e2ddf8b4cd3488' into api
[wolnelektury.git] / apps / catalogue / models.py
index 7e3ccfe..34c1d0b 100644 (file)
@@ -8,7 +8,6 @@ from django.utils.translation import ugettext_lazy as _
 from django.contrib.auth.models import User
 from django.core.files import File
 from django.template.loader import render_to_string
-from django.template.defaultfilters import slugify
 from django.utils.safestring import mark_safe
 from django.utils.translation import get_language
 from django.core.urlresolvers import reverse
@@ -22,6 +21,7 @@ from catalogue.fields import JSONField
 
 from librarian import dcparser, html, epub, NoDublinCore
 from mutagen import id3
+from slughifi import slughifi
 
 
 TAG_CATEGORIES = (
@@ -64,6 +64,9 @@ class Tag(TagBase):
     gazeta_link = models.CharField(blank=True, max_length=240)
     wiki_link = models.CharField(blank=True, max_length=240)
 
+    created_at    = models.DateTimeField(_('creation date'), auto_now_add=True, db_index=True)
+    changed_at    = models.DateTimeField(_('creation date'), auto_now=True, db_index=True)
+
     categories_rev = {
         'autor': 'author',
         'epoka': 'epoch',
@@ -156,26 +159,37 @@ class Tag(TagBase):
 
 
 # TODO: why is this hard-coded ?
-def book_upload_path(ext=None):
+def book_upload_path(ext=None, maxlen=100):
     def get_dynamic_path(media, filename, ext=ext):
         # how to put related book's slug here?
         if not ext:
-            ext = media.type
+            if media.type == 'daisy':
+                ext = 'daisy.zip'
+            else:
+                ext = media.type
         if not media.name:
-            name = slugify(filename.split(".")[0])
+            name = slughifi(filename.split(".")[0])
         else:
-            name = slugify(media.name)
-        return 'lektura/%s.%s' % (name, ext)
+            name = slughifi(media.name)
+        return 'lektura/%s.%s' % (name[:maxlen-len('lektura/.%s' % ext)-4], ext)
     return get_dynamic_path
 
 
 class BookMedia(models.Model):
     type        = models.CharField(_('type'), choices=MEDIA_FORMATS, max_length="100")
-    name        = models.CharField(_('name'), max_length="100", blank=True)
-    file        = models.FileField(_('file'), upload_to=book_upload_path(), blank=True)
+    name        = models.CharField(_('name'), max_length="100")
+    file        = models.FileField(_('file'), upload_to=book_upload_path())
     uploaded_at = models.DateTimeField(_('creation date'), auto_now_add=True, editable=False)
     extra_info  = JSONField(_('extra information'), default='{}')
 
+    def book_count(self):
+        return self.book_set.count()
+    book_count.short_description = _('book count')
+
+    def books(self):
+        return mark_safe('<br/>'.join("<a href='%s'>%s</a>" % (reverse('admin:catalogue_book_change', args=[b.id]), b.title) for b in self.book_set.all()))
+    books.short_description = _('books')
+
     def __unicode__(self):
         return "%s (%s)" % (self.name, self.file.name.split("/")[-1])
 
@@ -184,14 +198,14 @@ class BookMedia(models.Model):
         verbose_name        = _('book media')
         verbose_name_plural = _('book media')
 
-    def save(self, force_insert=False, force_update=False):
-        media = super(BookMedia, self).save(force_insert, force_update)
+    def save(self, force_insert=False, force_update=False, **kwargs):
+        media = super(BookMedia, self).save(force_insert, force_update, **kwargs)
         if self.type == 'mp3':
             file = self.file
             extra_info = self.get_extra_info_value()
             extra_info.update(self.get_mp3_info())
             self.set_extra_info_value(extra_info)
-            media = super(BookMedia, self).save(force_insert, force_update)
+            media = super(BookMedia, self).save(force_insert, force_update, **kwargs)
         return media
 
     def get_mp3_info(self):
@@ -209,7 +223,8 @@ class Book(models.Model):
     title         = models.CharField(_('title'), max_length=120)
     slug          = models.SlugField(_('slug'), max_length=120, unique=True, db_index=True)
     description   = models.TextField(_('description'), blank=True)
-    created_at    = models.DateTimeField(_('creation date'), auto_now_add=True)
+    created_at    = models.DateTimeField(_('creation date'), auto_now_add=True, db_index=True)
+    changed_at    = models.DateTimeField(_('creation date'), auto_now=True, db_index=True)
     _short_html   = models.TextField(_('short HTML'), editable=False)
     parent_number = models.IntegerField(_('parent number'), default=0)
     extra_info    = JSONField(_('extra information'))
@@ -453,8 +468,7 @@ class Book(models.Model):
         epub_file = StringIO()
         try:
             epub.transform(BookImportDocProvider(self), self.slug, output_file=epub_file)
-            self.epub_file.save('%s.epub' % self.slug, ContentFile(epub_file.getvalue()), save=False)
-            self.save()
+            self.epub_file.save('%s.epub' % self.slug, ContentFile(epub_file.getvalue()))
             FileRecord(slug=self.slug, type='epub', sha1=sha1(epub_file.getvalue()).hexdigest()).save()
         except NoDublinCore:
             pass
@@ -468,9 +482,19 @@ class Book(models.Model):
             child_book.save()
             book_descendants += list(child_book.children.all())
 
+    def build_txt(self):
+        from StringIO import StringIO
+        from django.core.files.base import ContentFile
+        from librarian import text
+
+        out = StringIO()
+        text.transform(open(self.xml_file.path), out)
+        self.txt_file.save('%s.txt' % self.slug, ContentFile(out.getvalue()))
+        self.save()
+
 
     @classmethod
-    def from_xml_file(cls, xml_file, overwrite=False, build_epub=True):
+    def from_xml_file(cls, xml_file, **kwargs):
         # use librarian to parse meta-data
         book_info = dcparser.parse(xml_file)
 
@@ -478,14 +502,14 @@ class Book(models.Model):
             xml_file = File(open(xml_file))
 
         try:
-            return cls.from_text_and_meta(xml_file, book_info, overwrite, build_epub=build_epub)
+            return cls.from_text_and_meta(xml_file, book_info, **kwargs)
         finally:
             xml_file.close()
 
     @classmethod
-    def from_text_and_meta(cls, raw_file, book_info, overwrite=False, build_epub=True):
+    def from_text_and_meta(cls, raw_file, book_info, overwrite=False, build_epub=True, build_txt=True):
+        import re
         from tempfile import NamedTemporaryFile
-        from slughifi import slughifi
         from markupstring import MarkupString
         from django.core.files.storage import default_storage
 
@@ -502,6 +526,8 @@ class Book(models.Model):
 
         # Read book metadata
         book_base, book_slug = book_info.url.rsplit('/', 1)
+        if re.search(r'[^a-zA-Z0-9-]', book_slug):
+            raise ValueError('Invalid characters in slug')
         book, created = Book.objects.get_or_create(slug=book_slug)
 
         if created:
@@ -536,7 +562,7 @@ class Book(models.Model):
                     tag.save()
                 book_tags.append(tag)
 
-        book.tags = book_tags + book_shelves
+        book.tags = set(book_tags + book_shelves)
 
         book_tag = book.book_tag()
 
@@ -592,6 +618,9 @@ class Book(models.Model):
                 new_fragment.save()
                 new_fragment.tags = set(book_tags + themes + [book_tag] + ancestor_tags)
 
+        if not settings.NO_BUILD_TXT and build_txt:
+            book.build_txt()
+
         if not settings.NO_BUILD_EPUB and build_epub:
             book.root_ancestor.build_epub()