class Tag(TagBase):
+ """A tag attachable to books and fragments (and possibly anything).
+
+ Used to represent searchable metadata (authors, epochs, genres, kinds),
+ fragment themes (motifs) and some book hierarchy related kludges."""
name = models.CharField(_('name'), max_length=50, db_index=True)
slug = models.SlugField(_('slug'), max_length=120, db_index=True)
sort_key = models.CharField(_('sort key'), max_length=120, db_index=True)
class BookMedia(models.Model):
+ """Represents media attached to a book."""
FileFormat = namedtuple("FileFormat", "name ext")
formats = SortedDict([
('mp3', FileFormat(name='MP3', ext='mp3')),
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)
slug = models.SlugField(_('slug'), max_length=120, db_index=True,
cover = models.FileField(_('cover'), upload_to=book_upload_path('png'),
null=True, blank=True)
- ebook_formats = ['pdf', 'epub', 'mobi', 'txt']
+ ebook_formats = ['pdf', 'epub', 'mobi', 'fb2', 'txt']
formats = ebook_formats + ['html', 'xml']
parent = models.ForeignKey('self', blank=True, null=True, related_name='children')
def build_html(self):
from django.core.files.base import ContentFile
from slughifi import slughifi
+ from sortify import sortify
from librarian import html
meta_tags = list(self.tags.filter(
tag, created = Tag.objects.get_or_create(slug=slughifi(theme_name), category='theme')
if created:
tag.name = theme_name
- tag.sort_key = theme_name.lower()
+ tag.sort_key = sortify(theme_name.lower())
tag.save()
themes.append(tag)
if not themes:
# Thin wrappers for builder tasks
def build_pdf(self, *args, **kwargs):
+ """(Re)builds PDF."""
return tasks.build_pdf.delay(self.pk, *args, **kwargs)
def build_epub(self, *args, **kwargs):
+ """(Re)builds EPUB."""
return tasks.build_epub.delay(self.pk, *args, **kwargs)
def build_mobi(self, *args, **kwargs):
+ """(Re)builds MOBI."""
return tasks.build_mobi.delay(self.pk, *args, **kwargs)
+ def build_fb2(self, *args, **kwargs):
+ """(Re)build FB2"""
+ return tasks.build_fb2.delay(self.pk, *args, **kwargs)
def build_txt(self, *args, **kwargs):
+ """(Re)builds TXT."""
return tasks.build_txt.delay(self.pk, *args, **kwargs)
@staticmethod
@classmethod
def from_text_and_meta(cls, raw_file, book_info, overwrite=False,
- build_epub=True, build_txt=True, build_pdf=True, build_mobi=True,
+ build_epub=True, build_txt=True, build_pdf=True, build_mobi=True, build_fb2=True,
search_index=True, search_index_tags=True, search_index_reuse=False):
# check for parts before we do anything
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)
@classmethod
def tagged_top_level(cls, tags):
- """ Returns top-level books tagged with `tags'.
+ """ Returns top-level books tagged with `tags`.
It only returns those books which don't have ancestors which are
also tagged with those tags.
def _has_factory(ftype):
has = lambda self: bool(getattr(self, "%s_file" % ftype))
- has.short_description = t.upper()
+ has.short_description = ftype.upper()
+ has.__doc__ = None
has.boolean = True
has.__name__ = "has_%s_file" % ftype
return has
class Fragment(models.Model):
+ """Represents a themed fragment of a book."""
text = models.TextField()
short_text = models.TextField(editable=False)
anchor = models.CharField(max_length=120)