X-Git-Url: https://git.mdrn.pl/wolnelektury.git/blobdiff_plain/938fc832934e508085dad3645c01060a4dc9f0c9..ffb9b20e6675a618694fb160c72df285f2666be8:/apps/catalogue/models.py?ds=inline diff --git a/apps/catalogue/models.py b/apps/catalogue/models.py index 43a9496d8..7f20b4e59 100644 --- a/apps/catalogue/models.py +++ b/apps/catalogue/models.py @@ -7,7 +7,7 @@ from collections import namedtuple from django.db import models from django.db.models import permalink, Q import django.dispatch -from django.core.cache import cache +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 @@ -17,20 +17,23 @@ 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 +import jsonfield from django.conf import settings 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 -from catalogue.tasks import touch_tag +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 import re from os import path +import search +# Those are hard-coded here so that makemessages sees them. TAG_CATEGORIES = ( ('author', _('author')), ('epoch', _('epoch')), @@ -41,8 +44,8 @@ TAG_CATEGORIES = ( ('book', _('book')), ) -# not quite, but Django wants you to set a timeout -CACHE_FOREVER = 2419200 # 28 days + +permanent_cache = get_cache('permanent') class TagSubcategoryManager(models.Manager): @@ -369,6 +372,9 @@ class Book(models.Model): formats = ebook_formats + ['html', 'xml'] parent = models.ForeignKey('self', blank=True, null=True, related_name='children') + + _related_info = jsonfield.JSONField(blank=True, null=True, editable=False) + objects = models.Manager() tagged = managers.ModelTaggedItemManager(Tag) tags = managers.TagDescriptor(Tag) @@ -447,62 +453,11 @@ class Book(models.Model): if self.id is None: return - cache_key = "Book.short_html/%d/%s" - for lang, langname in settings.LANGUAGES: - cache.delete(cache_key % (self.id, lang)) - cache.delete("Book.mini_box/%d" % (self.id, )) + type(self).objects.filter(pk=self.pk).update(_related_info=None) # Fragment.short_html relies on book's tags, so reset it here too for fragm in self.fragments.all(): fragm.reset_short_html() - def short_html(self): - if self.id: - cache_key = "Book.short_html/%d/%s" % (self.id, get_language()) - short_html = cache.get(cache_key) - else: - short_html = None - - if short_html is not None: - return mark_safe(short_html) - else: - tags = self.tags.filter(category__in=('author', 'kind', 'genre', 'epoch')) - tags = split_tags(tags) - - formats = [] - # files generated during publication - for ebook_format in self.ebook_formats: - if self.has_media(ebook_format): - formats.append(u'%s' % ( - self.get_media(ebook_format).url, - ebook_format.upper() - )) - - formats = [mark_safe(format) for format in formats] - - short_html = unicode(render_to_string('catalogue/book_short.html', - {'book': self, 'tags': tags, 'formats': formats})) - - if self.id: - cache.set(cache_key, short_html, CACHE_FOREVER) - return mark_safe(short_html) - - def mini_box(self): - if self.id: - cache_key = "Book.mini_box/%d" % (self.id, ) - short_html = cache.get(cache_key) - else: - short_html = None - - if short_html is None: - authors = self.tags.filter(category='author') - - short_html = unicode(render_to_string('catalogue/book_mini_box.html', - {'book': self, 'authors': authors, 'STATIC_URL': settings.STATIC_URL})) - - if self.id: - cache.set(cache_key, short_html, CACHE_FOREVER) - return mark_safe(short_html) - def has_description(self): return len(self.description) > 0 has_description.short_description = _('description') @@ -610,7 +565,6 @@ class Book(models.Model): def build_html(self): - from markupstring import MarkupString from django.core.files.base import ContentFile from slughifi import slughifi from librarian import html @@ -654,9 +608,9 @@ class Book(models.Model): continue text = fragment.to_string() - short_text = '' - if (len(MarkupString(text)) > 240): - short_text = unicode(MarkupString(text)[:160]) + short_text = truncate_html_words(text, 15) + if text == short_text: + short_text = '' new_fragment = Fragment.objects.create(anchor=fragment.id, book=self, text=text, short_text=short_text) @@ -689,6 +643,19 @@ class Book(models.Model): result = create_zip.delay(paths, "%s_%s" % (self.slug, format_)) return result.wait() + def search_index(self, book_info=None, reuse_index=False): + if reuse_index: + idx = search.ReusableIndex() + else: + idx = search.Index() + + idx.open() + try: + idx.index_book(self, book_info) + idx.index_tags() + finally: + idx.close() + @classmethod def from_xml_file(cls, xml_file, **kwargs): from django.core.files import File @@ -707,7 +674,8 @@ class Book(models.Model): @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, + search_index=True): import re from sortify import sortify @@ -778,6 +746,10 @@ class Book(models.Model): if not settings.NO_BUILD_MOBI and build_mobi: book.build_mobi() + if not settings.NO_SEARCH_INDEX and search_index: + book.search_index() + #index_book.delay(book.id, book_info) + book_descendants = list(book.children.all()) descendants_tags = set() # add l-tag to descendants and their fragments @@ -802,12 +774,42 @@ class Book(models.Model): cls.published.send(sender=book) return book + def related_info(self): + """Keeps info about related objects (tags, media) in cache field.""" + if self._related_info is not None: + return self._related_info + else: + rel = {'tags': {}, 'media': {}} + + tags = self.tags.filter(category__in=( + 'author', 'kind', 'genre', 'epoch')) + tags = split_tags(tags) + for category in tags: + rel['tags'][category] = [ + (t.name, t.slug) for t in tags[category]] + + for media_format in BookMedia.formats: + rel['media'][media_format] = self.has_media(media_format) + + book = self + parents = [] + while book.parent: + parents.append((book.parent.title, book.parent.slug)) + book = book.parent + parents = parents[::-1] + if parents: + rel['parents'] = parents + + if self.pk: + type(self).objects.filter(pk=self.pk).update(_related_info=rel) + return rel + def reset_tag_counter(self): if self.id is None: return cache_key = "Book.tag_counter/%d" % self.id - cache.delete(cache_key) + permanent_cache.delete(cache_key) if self.parent: self.parent.reset_tag_counter() @@ -815,7 +817,7 @@ class Book(models.Model): def tag_counter(self): if self.id: cache_key = "Book.tag_counter/%d" % self.id - tags = cache.get(cache_key) + tags = permanent_cache.get(cache_key) else: tags = None @@ -828,7 +830,7 @@ class Book(models.Model): tags[tag.pk] = 1 if self.id: - cache.set(cache_key, tags, CACHE_FOREVER) + permanent_cache.set(cache_key, tags) return tags def reset_theme_counter(self): @@ -836,7 +838,7 @@ class Book(models.Model): return cache_key = "Book.theme_counter/%d" % self.id - cache.delete(cache_key) + permanent_cache.delete(cache_key) if self.parent: self.parent.reset_theme_counter() @@ -844,7 +846,7 @@ class Book(models.Model): def theme_counter(self): if self.id: cache_key = "Book.theme_counter/%d" % self.id - tags = cache.get(cache_key) + tags = permanent_cache.get(cache_key) else: tags = None @@ -855,7 +857,7 @@ class Book(models.Model): tags[tag.pk] = tags.get(tag.pk, 0) + 1 if self.id: - cache.set(cache_key, tags, CACHE_FOREVER) + permanent_cache.set(cache_key, tags) return tags def pretty_title(self, html_links=False): @@ -944,6 +946,16 @@ class Book(models.Model): audiences = sorted(set([self._audiences_pl[a] for a in audiences])) return [a[1] for a in audiences] + def choose_fragment(self): + tag = self.book_tag() + fragments = Fragment.tagged.with_any([tag]) + if fragments.exists(): + return fragments.order_by('?')[0] + elif self.parent: + return self.parent.choose_fragment() + else: + return None + def _has_factory(ftype): has = lambda self: bool(getattr(self, "%s_file" % ftype)) @@ -979,7 +991,7 @@ class Fragment(models.Model): verbose_name_plural = _('fragments') def get_absolute_url(self): - return '%s#m%s' % (self.book.get_html_url(), self.anchor) + return '%s#m%s' % (reverse('book_text', args=[self.book.slug]), self.anchor) def reset_short_html(self): if self.id is None: @@ -987,12 +999,16 @@ class Fragment(models.Model): cache_key = "Fragment.short_html/%d/%s" for lang, langname in settings.LANGUAGES: - cache.delete(cache_key % (self.id, lang)) + permanent_cache.delete(cache_key % (self.id, lang)) + + def get_short_text(self): + """Returns short version of the fragment.""" + return self.short_text if self.short_text else self.text def short_html(self): if self.id: cache_key = "Fragment.short_html/%d/%s" % (self.id, get_language()) - short_html = cache.get(cache_key) + short_html = permanent_cache.get(cache_key) else: short_html = None @@ -1002,10 +1018,28 @@ class Fragment(models.Model): short_html = unicode(render_to_string('catalogue/fragment_short.html', {'fragment': self})) if self.id: - cache.set(cache_key, short_html, CACHE_FOREVER) + permanent_cache.set(cache_key, short_html) return mark_safe(short_html) +class Collection(models.Model): + """A collection of books, which might be defined before publishing them.""" + title = models.CharField(_('title'), max_length=120, db_index=True) + slug = models.SlugField(_('slug'), max_length=120, primary_key=True) + description = models.TextField(_('description'), null=True, blank=True) + + models.SlugField(_('slug'), max_length=120, unique=True, db_index=True) + book_slugs = models.TextField(_('book slugs')) + + class Meta: + ordering = ('title',) + verbose_name = _('collection') + verbose_name_plural = _('collections') + + def __unicode__(self): + return self.title + + ########### # # SIGNALS