X-Git-Url: https://git.mdrn.pl/wolnelektury.git/blobdiff_plain/0e8c1e1029c9a8039600d87553f206410647e6b4..3af72840ecad9b3122fdcfa6a4c2946f6113eac6:/apps/catalogue/models.py diff --git a/apps/catalogue/models.py b/apps/catalogue/models.py index 90ecb1a3c..4ef0a14e6 100644 --- a/apps/catalogue/models.py +++ b/apps/catalogue/models.py @@ -13,11 +13,11 @@ from django.utils.translation import get_language from django.core.urlresolvers import reverse from datetime import datetime -from newtagging.models import TagBase +from newtagging.models import TagBase, tags_updated from newtagging import managers from catalogue.fields import JSONField -from librarian import html, dcparser +from librarian import dcparser, html, epub, NoDublinCore from mutagen import id3 @@ -51,7 +51,7 @@ class Tag(TagBase): main_page = models.BooleanField(_('main page'), default=False, db_index=True, help_text=_('Show tag on main page')) user = models.ForeignKey(User, blank=True, null=True) - book_count = models.IntegerField(_('book count'), default=0, blank=False, null=False) + book_count = models.IntegerField(_('book count'), blank=False, null=True) death = models.IntegerField(_(u'year of death'), blank=True, null=True) gazeta_link = models.CharField(blank=True, max_length=240) wiki_link = models.CharField(blank=True, max_length=240) @@ -98,6 +98,27 @@ class Tag(TagBase): """ calculates the year of public domain entry for an author """ return self.death + 71 if self.death is not None else None + def get_count(self): + """ returns global book count for book tags, fragment count for themes """ + + if self.book_count is None: + if self.category == 'book': + # never used + objects = Book.objects.none() + elif self.category == 'theme': + objects = Fragment.tagged.with_all((self,)) + else: + objects = Book.tagged.with_all((self,)).order_by() + if self.category != 'set': + # eliminate descendants + l_tags = Tag.objects.filter(slug__in=[book.book_tag_slug() for book in objects]) + descendants_keys = [book.pk for book in Book.tagged.with_any(l_tags)] + if descendants_keys: + objects = objects.exclude(pk__in=descendants_keys) + self.book_count = objects.count() + self.save() + return self.book_count + @staticmethod def get_tag_list(tags): if isinstance(tags, basestring): @@ -186,7 +207,7 @@ class Book(models.Model): def __unicode__(self): return self.title - def save(self, force_insert=False, force_update=False, reset_short_html=True, refresh_mp3=True): + def save(self, force_insert=False, force_update=False, reset_short_html=True, refresh_mp3=True, **kwargs): if reset_short_html: # Reset _short_html during save update = {} @@ -215,8 +236,11 @@ class Book(models.Model): def name(self): return self.title + def book_tag_slug(self): + return ('l-' + self.slug)[:120] + def book_tag(self): - slug = ('l-' + self.slug)[:120] + slug = self.book_tag_slug() book_tag, created = Tag.objects.get_or_create(slug=slug, category='book') if created: book_tag.name = self.title[:50] @@ -296,7 +320,7 @@ class Book(models.Model): book_info = dcparser.parse(xml_file) if not isinstance(xml_file, File): - xml_file = File(xml_file) + xml_file = File(open(xml_file)) try: return cls.from_text_and_meta(xml_file, book_info, overwrite) @@ -308,6 +332,10 @@ class Book(models.Model): from tempfile import NamedTemporaryFile from slughifi import slughifi from markupstring import MarkupString + from hashlib import sha1 + from django.core.files.base import ContentFile + from django.core.files.storage import default_storage + from StringIO import StringIO # Read book metadata book_base, book_slug = book_info.url.rsplit('/', 1) @@ -345,7 +373,7 @@ class Book(models.Model): tag.save() book_tags.append(tag) - book.tags = book_tags + book.tags = book_tags + book_shelves book_tag = book.book_tag() @@ -376,35 +404,48 @@ class Book(models.Model): if html.transform(book.xml_file.path, html_file, parse_dublincore=False): book.html_file.save('%s.html' % book.slug, File(html_file), save=False) + # Create EPUB + epub_file = StringIO() + try: + epub.transform(book.xml_file, epub_file) + book.epub_file.save('%s.epub' % book.slug, ContentFile(epub_file.getvalue()), save=False) + FileRecord(slug=book.slug, type='epub', sha1=sha1(epub_file.getvalue()).hexdigest()).save() + except NoDublinCore: + pass + # Extract fragments closed_fragments, open_fragments = html.extract_fragments(book.html_file.path) - book_themes = [] for fragment in closed_fragments.values(): - text = fragment.to_string() - short_text = '' - if (len(MarkupString(text)) > 240): - short_text = unicode(MarkupString(text)[:160]) - new_fragment, created = Fragment.objects.get_or_create(anchor=fragment.id, book=book, - defaults={'text': text, 'short_text': short_text}) - try: theme_names = [s.strip() for s in fragment.themes.split(',')] except AttributeError: continue themes = [] for theme_name in theme_names: + if not theme_name: + continue tag, created = Tag.objects.get_or_create(slug=slughifi(theme_name), category='theme') if created: tag.name = theme_name tag.sort_key = slughifi(theme_name) tag.save() themes.append(tag) + if not themes: + continue + + text = fragment.to_string() + short_text = '' + if (len(MarkupString(text)) > 240): + short_text = unicode(MarkupString(text)[:160]) + new_fragment, created = Fragment.objects.get_or_create(anchor=fragment.id, book=book, + defaults={'text': text, 'short_text': short_text}) + new_fragment.save() - new_fragment.tags = set(list(book.tags) + themes + [book_tag]) - book_themes += themes + new_fragment.tags = set(book_tags + themes + [book_tag]) - book_themes = set(book_themes) - book.tags = list(book.tags) + list(book_themes) + book_shelves + # refresh cache + book.tag_counter + book.theme_counter book.save() return book @@ -421,6 +462,12 @@ class Book(models.Model): self.save(reset_short_html=False, refresh_mp3=False) return tags + def reset_tag_counter(self): + self._tag_counter = None + self.save(reset_short_html=False, refresh_mp3=False) + if self.parent: + self.parent.reset_tag_counter() + @property def tag_counter(self): if self._tag_counter is None: @@ -436,6 +483,12 @@ class Book(models.Model): self.save(reset_short_html=False, refresh_mp3=False) return tags + def reset_theme_counter(self): + self._theme_counter = None + self.save(reset_short_html=False, refresh_mp3=False) + if self.parent: + self.parent.reset_theme_counter() + @property def theme_counter(self): if self._theme_counter is None: @@ -503,3 +556,34 @@ class BookStub(models.Model): return self.title +class FileRecord(models.Model): + slug = models.SlugField(_('slug'), max_length=120, db_index=True) + type = models.CharField(_('type'), max_length=20, db_index=True) + sha1 = models.CharField(_('sha-1 hash'), max_length=40) + time = models.DateTimeField(_('time'), auto_now_add=True) + + class Meta: + ordering = ('-time','-slug', '-type') + verbose_name = _('file record') + verbose_name_plural = _('file records') + + def __unicode__(self): + return "%s %s.%s" % (self.sha1, self.slug, self.type) + + +def _tags_updated_handler(sender, affected_tags, **kwargs): + # reset tag global counter + Tag.objects.filter(pk__in=[tag.pk for tag in affected_tags]).update(book_count=None) + + # if book tags changed, reset book tag counter + if isinstance(sender, Book) and \ + Tag.objects.filter(pk__in=(tag.pk for tag in affected_tags)).\ + exclude(category__in=('book', 'theme', 'set')).count(): + sender.reset_tag_counter() + # if fragment theme changed, reset book theme counter + elif isinstance(sender, Fragment) and \ + Tag.objects.filter(pk__in=(tag.pk for tag in affected_tags)).\ + filter(category='theme').count(): + sender.book.reset_theme_counter() +tags_updated.connect(_tags_updated_handler) +