Merge branch 'master' of github.com:fnp/wolnelektury
authorŁukasz Rekucki <lrekucki@gmail.com>
Fri, 18 Jun 2010 10:41:32 +0000 (12:41 +0200)
committerŁukasz Rekucki <lrekucki@gmail.com>
Fri, 18 Jun 2010 10:41:32 +0000 (12:41 +0200)
12 files changed:
apps/catalogue/models.py
apps/catalogue/templatetags/catalogue_tags.py
apps/catalogue/test_utils.py
apps/catalogue/tests/__init__.py
apps/catalogue/tests/book_import.py
apps/catalogue/tests/tags.py
apps/catalogue/tests/templatetags.py [new file with mode: 0644]
wolnelektury/manage.py
wolnelektury/static/js/book.js
wolnelektury/static/js/catalogue.js
wolnelektury/templates/catalogue/book_detail.html
wolnelektury/templates/catalogue/fragment_short.html

index 367f382..90ecb1a 100644 (file)
@@ -189,8 +189,12 @@ class Book(models.Model):
     def save(self, force_insert=False, force_update=False, reset_short_html=True, refresh_mp3=True):
         if reset_short_html:
             # Reset _short_html during save
+            update = {}
             for key in filter(lambda x: x.startswith('_short_html'), self.__dict__):
+                update[key] = ''
                 self.__setattr__(key, '')
+            # Fragment.short_html relies on book's tags, so reset it here too
+            self.fragments.all().update(**update)
 
         book = super(Book, self).save(force_insert, force_update)
 
@@ -323,18 +327,23 @@ class Book(models.Model):
         book.save()
 
         book_tags = []
-        for category in ('kind', 'genre', 'author', 'epoch'):
-            tag_name = getattr(book_info, category)
-            tag_sort_key = tag_name
-            if category == 'author':
-                tag_sort_key = tag_name.last_name
-                tag_name = ' '.join(tag_name.first_names) + ' ' + tag_name.last_name
-            tag, created = Tag.objects.get_or_create(slug=slughifi(tag_name), category=category)
-            if created:
-                tag.name = tag_name
-                tag.sort_key = slughifi(tag_sort_key)
-                tag.save()
-            book_tags.append(tag)
+        categories = (('kinds', 'kind'), ('genres', 'genre'), ('authors', 'author'), ('epochs', 'epoch'))
+        for field_name, category in categories:
+            try:
+                tag_names = getattr(book_info, field_name)
+            except:
+                tag_names = [getattr(book_info, category)]
+            for tag_name in tag_names:
+                tag_sort_key = tag_name
+                if category == 'author':
+                    tag_sort_key = tag_name.last_name
+                    tag_name = ' '.join(tag_name.first_names) + ' ' + tag_name.last_name
+                tag, created = Tag.objects.get_or_create(slug=slughifi(tag_name), category=category)
+                if created:
+                    tag.name = tag_name
+                    tag.sort_key = slughifi(tag_sort_key)
+                    tag.save()
+                book_tags.append(tag)
 
         book.tags = book_tags
 
@@ -460,11 +469,8 @@ class Fragment(models.Model):
         if short_html and len(short_html):
             return mark_safe(short_html)
         else:
-            book_authors = [mark_safe(u'<a href="%s">%s</a>' % (tag.get_absolute_url(), tag.name))
-                for tag in self.book.tags if tag.category == 'author']
-
             setattr(self, key, unicode(render_to_string('catalogue/fragment_short.html',
-                {'fragment': self, 'book': self.book, 'book_authors': book_authors})))
+                {'fragment': self})))
             self.save()
             return mark_safe(getattr(self, key))
 
index 7f2bf29..36a015a 100644 (file)
@@ -61,6 +61,29 @@ def simple_title(tags):
     return capfirst(', '.join(title))
 
 
+@register.simple_tag
+def book_title(book, html_links=False):
+    names = list(book.tags.filter(category='author'))
+
+    books = []
+    while book:
+        books.append(book)
+        book = book.parent
+    names.extend(reversed(books))
+
+    if html_links:
+        names = ['<a href="%s">%s</a>' % (tag.get_absolute_url(), tag.name) for tag in names]
+    else:
+        names = [tag.name for tag in names]
+
+    return ', '.join(names)
+
+
+@register.simple_tag
+def book_title_html(book):
+    return book_title(book, html_links=True)
+
+
 @register.simple_tag
 def title_from_tags(tags):
     def split_tags(tags):
index 3a8af57..398a0fe 100644 (file)
@@ -2,6 +2,7 @@ from django.conf import settings
 from django.test import TestCase
 import shutil
 import tempfile
+from slughifi import slughifi
 
 class WLTestCase(TestCase):
     """
@@ -36,3 +37,14 @@ class BookInfoStub(object):
 
     def to_dict(self):
         return dict((key, unicode(value)) for key, value in self.__dict.items())
+
+
+def info_args(title):
+    """ generate some keywords for comfortable BookInfoCreation  """
+    slug = unicode(slughifi(title))
+    return {
+        'title': unicode(title),
+        'slug': slug,
+        'url': u"http://wolnelektury.pl/example/%s" % slug,
+        'about': u"http://wolnelektury.pl/example/URI/%s" % slug,
+    }
index d656d45..2366653 100644 (file)
@@ -1,3 +1,4 @@
 from catalogue.tests.book_import import *
-from catalogue.tests.tags import *
 from catalogue.tests.search import *
+from catalogue.tests.tags import *
+from catalogue.tests.templatetags import *
index fed9922..e5fa031 100644 (file)
@@ -82,7 +82,7 @@ class BookImportLogicTests(WLTestCase):
         BOOK_TEXT = """<utwor />"""
         book = models.Book.from_text_and_meta(ContentFile(BOOK_TEXT), self.book_info)
         self.book_info.title = u"Extraordinary"
-        book = models.Book.from_text_and_meta(ContentFile(BOOK_TEXT), self.book_info)
+        book = models.Book.from_text_and_meta(ContentFile(BOOK_TEXT), self.book_info, overwrite=True)
 
         tags = [ (tag.category, tag.slug) for tag in book.tags ]
         tags.sort()
@@ -93,7 +93,7 @@ class BookImportLogicTests(WLTestCase):
         BOOK_TEXT = """<utwor />"""
         book = models.Book.from_text_and_meta(ContentFile(BOOK_TEXT), self.book_info)
         self.book_info.author = PersonStub(("Hans", "Christian"), "Andersen")
-        book = models.Book.from_text_and_meta(ContentFile(BOOK_TEXT), self.book_info)
+        book = models.Book.from_text_and_meta(ContentFile(BOOK_TEXT), self.book_info, overwrite=True)
 
         tags = [ (tag.category, tag.slug) for tag in book.tags ]
         tags.sort()
@@ -104,6 +104,26 @@ class BookImportLogicTests(WLTestCase):
 
         self.assertEqual(tags, self.expected_tags)
 
-        # the old tag should disappear
-        self.assertRaises(models.Tag.DoesNotExist, models.Tag.objects.get,
-                    slug="jim-lazy", category="author")
+        # the old tag shouldn't disappear
+        models.Tag.objects.get(slug="jim-lazy", category="author")
+
+    def test_multiple_tags(self):
+        BOOK_TEXT = """<utwor />"""
+        self.book_info.authors = self.book_info.author, PersonStub(("Joe",), "Dilligent"),
+        self.book_info.kinds = self.book_info.kind, 'Y-Kind',
+        self.book_info.genres = self.book_info.genre, 'Y-Genre',
+        self.book_info.epochs = self.book_info.epoch, 'Y-Epoch',
+
+        self.expected_tags.extend([
+           ('author', 'joe-dilligent'),
+           ('genre', 'y-genre'),
+           ('epoch', 'y-epoch'),
+           ('kind', 'y-kind'),
+        ])
+        self.expected_tags.sort()
+
+        book = models.Book.from_text_and_meta(ContentFile(BOOK_TEXT), self.book_info)
+        tags = [ (tag.category, tag.slug) for tag in book.tags ]
+        tags.sort()
+
+        self.assertEqual(tags, self.expected_tags)
index f9102a7..00d1167 100644 (file)
@@ -2,20 +2,9 @@
 from catalogue import models
 from catalogue.test_utils import *
 from django.core.files.base import ContentFile
-from slughifi import slughifi
 
 from nose.tools import raises
 
-def info_args(title):
-    """ generate some keywords for comfortable BookInfoCreation  """
-    slug = unicode(slughifi(title))
-    return {
-        'title': unicode(title),
-        'slug': slug,
-        'url': u"http://wolnelektury.pl/example/%s" % slug,
-        'about': u"http://wolnelektury.pl/example/URI/%s" % slug,
-    }
-
 
 class BooksByTagTests(WLTestCase):
     """ tests the /katalog/tag page for found books """
@@ -23,38 +12,20 @@ class BooksByTagTests(WLTestCase):
     def setUp(self):
         WLTestCase.setUp(self)
         author = PersonStub(("Common",), "Man")
-        tags = dict(genre='G', epoch='E', author=author, kind="K")
 
         # grandchild
-        kwargs = info_args(u"GChild")
-        kwargs.update(tags)
-        gchild_info = BookInfoStub(**kwargs)
+        self.gchild_info = BookInfoStub(genre='Genre', epoch='Epoch', kind='Kind', author=author,
+                                        **info_args("GChild"))
         # child
-        kwargs = info_args(u"Child")
-        kwargs.update(tags)
-        child_info = BookInfoStub(parts=[gchild_info.url], **kwargs)
-        # other grandchild
-        kwargs = info_args(u"Different GChild")
-        kwargs.update(tags)
-        diffgchild_info = BookInfoStub(**kwargs)
-        # other child
-        kwargs = info_args(u"Different Child")
-        kwargs.update(tags)
-        kwargs['kind'] = 'K2'
-        diffchild_info = BookInfoStub(parts=[diffgchild_info.url], **kwargs)
+        self.child_info = BookInfoStub(genre='Genre', epoch='Epoch', kind='Other Kind', author=author,
+                                       parts=[self.gchild_info.url],
+                                       **info_args("Child"))
         # parent
-        kwargs = info_args(u"Parent")
-        kwargs.update(tags)
-        parent_info = BookInfoStub(parts=[child_info.url, diffchild_info.url], **kwargs)
-
-        # create the books
-        book_file = ContentFile('<utwor />')
-        for info in gchild_info, child_info, diffgchild_info, diffchild_info, parent_info:
-            book = models.Book.from_text_and_meta(book_file, info)
+        self.parent_info = BookInfoStub(genre='Genre', epoch='Epoch', kind='Kind', author=author,
+                                        parts=[self.child_info.url],
+                                        **info_args("Parent"))
 
-        # useful tags
-        self.author = models.Tag.objects.get(name='Common Man', category='author')
-        models.Tag.objects.create(name='Empty tag', slug='empty', category='author')
+        self.book_file = ContentFile('<utwor />')
 
     def test_nonexistent_tag(self):
         """ Looking for a non-existent tag should yield 404 """
@@ -63,30 +34,37 @@ class BooksByTagTests(WLTestCase):
 
     def test_book_tag(self):
         """ Looking for a book tag isn't permitted """
-        self.assertEqual(404, self.client.get('/katalog/parent/').status_code)
+        models.Book.from_text_and_meta(self.book_file, self.gchild_info)
+        self.assertEqual(404, self.client.get('/katalog/gchild/').status_code)
 
     def test_tag_empty(self):
         """ Tag with no books should return no books """
+        models.Book.from_text_and_meta(self.book_file, self.gchild_info)
+        models.Tag.objects.create(name='Empty tag', slug='empty', category='author')
+
         context = self.client.get('/katalog/empty/').context
         self.assertEqual(0, len(context['object_list']))
 
-    def test_tag_common(self):
-        """ Filtering by tag should only yield top-level books. """
-        context = self.client.get('/katalog/%s/' % self.author.slug).context
+    def test_tag_eliminate(self):
+        """ Filtering by tag should only yield top-level qualifying books. """
+        for info in self.gchild_info, self.child_info, self.parent_info:
+            models.Book.from_text_and_meta(self.book_file, info)
+
+        # all three qualify
+        context = self.client.get('/katalog/genre/').context
         self.assertEqual([book.title for book in context['object_list']],
                          ['Parent'])
 
-    def test_tag_child(self):
-        """ Filtering by child's tag should yield the child """
-        context = self.client.get('/katalog/k2/').context
+        # parent and gchild qualify, child doesn't
+        context = self.client.get('/katalog/kind/').context
         self.assertEqual([book.title for book in context['object_list']],
-                         ['Different Child'])
+                         ['Parent'])
 
-    def test_tag_child_jump(self):
-        """ Of parent and grandchild, only parent should be returned. """
-        context = self.client.get('/katalog/k/').context
+        # Filtering by child's tag should yield the child
+        context = self.client.get('/katalog/other-kind/').context
         self.assertEqual([book.title for book in context['object_list']],
-                         ['Parent'])
+                         ['Child'])
+
 
 
 class TagRelatedTagsTests(WLTestCase):
@@ -171,17 +149,14 @@ class TagRelatedTagsTests(WLTestCase):
                          'wrong related tag epoch tag on tag page')
 
 
-    def test_siblings_tags_add(self):
+    def test_siblings_tags_count(self):
         """ if children have tags and parent hasn't, count the children """
 
         cats = self.client.get('/katalog/epoch/').context['categories']
         self.assertTrue(('ChildKind', 2) in [(tag.name, tag.count) for tag in cats['kind']],
                     'wrong related kind tags on tag page')
 
-    def test_themes_add(self):
-        """ all occurencies of theme should be counted """
-
-        cats = self.client.get('/katalog/epoch/').context['categories']
+        # all occurencies of theme should be counted
         self.assertTrue(('Theme', 4) in [(tag.name, tag.count) for tag in cats['theme']],
                     'wrong related theme count')
 
@@ -244,7 +219,7 @@ class TestIdenticalTag(WLTestCase):
 
         cats = self.client.get('/katalog/lektura/tag/').context['categories']
         for category in 'author', 'kind', 'genre', 'epoch', 'theme':
-            self.assertTrue('tag' in [tag.name for tag in cats[category]],
+            self.assertTrue('tag' in [tag.slug for tag in cats[category]],
                             'missing related tag for %s' % category)
 
     def test_qualified_url(self):
diff --git a/apps/catalogue/tests/templatetags.py b/apps/catalogue/tests/templatetags.py
new file mode 100644 (file)
index 0000000..7a2ac36
--- /dev/null
@@ -0,0 +1,27 @@
+# -*- coding: utf-8 -*-
+from catalogue import models
+from catalogue.templatetags import catalogue_tags
+from catalogue.test_utils import *
+from django.core.files.base import ContentFile
+
+
+class BookDescTests(WLTestCase):
+    """ tests book_title template tag """
+
+    def setUp(self):
+        WLTestCase.setUp(self)
+        authors = PersonStub(("Common",), "Man"), PersonStub(("Jane",), "Doe")
+
+        child_info = BookInfoStub(authors=authors, genre="Genre", epoch='Epoch', kind="Kind",
+                                   **info_args(u"Child"))
+        parent_info = BookInfoStub(authors=authors, genre="Genre", epoch='Epoch', kind="Kind",
+                                   parts=[child_info.url],
+                                   **info_args(u"Parent"))
+
+        self.child = models.Book.from_text_and_meta(ContentFile('<utwor/>'), child_info)
+        models.Book.from_text_and_meta(ContentFile('<utwor/>'), parent_info)
+        self.child = models.Book.objects.get(pk=self.child.pk)
+
+    def test_book_desc(self):
+        """ book description should return authors, ancestors, book """
+        self.assertEqual(catalogue_tags.book_title(self.child), 'Jane Doe, Common Man, Parent, Child')
index 93b2805..309a56e 100755 (executable)
@@ -2,7 +2,7 @@
 import os.path
 import sys
 
-ROOT = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
+ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
 
 # Add apps and lib directories to PYTHONPATH
 sys.path = [
index 598ef2e..9960bfa 100644 (file)
@@ -1,10 +1,14 @@
 $(function() {
     function scrollToAnchor(anchor) {
         if (anchor) {
-            var element = $('a[name="' + anchor.slice(1) + '"]');
+            var anchor_name = anchor.slice(1);
+            var element = $('a[name="' + anchor_name + '"]');
             if (element.length > 0) {
                 $.scrollTo(element, 500, {offset: {top: -50, left: 0}});
-                $(element).highlightFade('yellow');
+                foot_elem = $('#footnotes a[name="' + anchor_name + '"]');
+                if (foot_elem.length > 0) {
+                    $(element).parent().highlightFade('yellow');
+                }
                 window.location.hash = anchor;
             }
         }
index 6e0f562..55ecef6 100644 (file)
@@ -312,42 +312,45 @@ function serverTime() {
             location.href = $('h2 a', this).attr('href');
         });
 
-               function toggled_by_slide(cont, short_el, long_el, button, short_text, long_text) {
-                       function toggle(cont, short_el, long_el, button, short_text, long_text) {
-                   if (cont.hasClass('short')) {
-                       cont.animate({"height": long_el.attr("cont_h")+'px'}, {duration: "fast" }).removeClass('short');
-                       short_el.hide();
-                       long_el.show();
-                       button.html(long_text);
-                   } else {
-                       cont.animate({"height": short_el.attr("cont_h")+'px'}, {duration: "fast" }).addClass('short');
-                       long_el.hide();
-                       short_el.show();
-                       button.html(short_text);
-                   }
-                       }
+        function toggled_by_slide(cont, short_el, long_el, button, short_text, long_text) {
+            function toggle(cont, short_el, long_el, button, short_text, long_text) {
+                if (cont.hasClass('short')) {
+                    cont.animate({"height": long_el.attr("cont_h")+'px'}, {duration: "fast" }).removeClass('short');
+                    short_el.hide();
+                    long_el.show();
+                    button.html(long_text);
+                } else {
+                    cont.animate({"height": short_el.attr("cont_h")+'px'}, {duration: "fast" }).addClass('short');
+                    long_el.hide();
+                    short_el.show();
+                    button.html(short_text);
+                }
+            }
+            if (long_el.html().length <= short_el.html().length)
+                return;
+
             long_el.attr("cont_h", cont.height()).hide();
             short_el.show().attr("cont_h", cont.height());
-                       cont.addClass('short');
-                       button.html(short_text);
-                       button.hover(
+            cont.addClass('short');
+            button.html(short_text);
+            button.hover(
                 function() { $(this).css({background: '#F3F3F3', cursor: 'pointer'}); },
                 function() { $(this).css({background: '#EEE'}); }
-                       ).click(function(){
-                               toggle(cont, short_el, long_el, button, short_text, long_text)
-                       });
-                       cont.hover(
+            ).click(function(){
+                toggle(cont, short_el, long_el, button, short_text, long_text)
+            });
+            cont.hover(
                 function() { $(this).css({background: '#F3F3F3', cursor: 'pointer'}); },
                 function() { $(this).css({background: '#FFF'}); }
             ).click(function(){
                 toggle(cont, short_el, long_el, button, short_text, long_text)
             })
-               }
+        }
         toggled_by_slide($('#description'), $('#description-short'), $('#description-long'),
           $('#toggle-description p'),
           LOCALE_TEXTS[LANGUAGE_CODE]['EXPAND_DESCRIPTION']+' ▼',
-                 LOCALE_TEXTS[LANGUAGE_CODE]['HIDE_DESCRIPTION'] + ' ▲'
-                 );
+            LOCALE_TEXTS[LANGUAGE_CODE]['HIDE_DESCRIPTION'] + ' ▲'
+        );
 
         $('#toggle-share-shelf').hover(
             function() { $(this).css({background: '#F3F3F3', cursor: 'pointer'}); },
index 809bd1a..4a8a931 100644 (file)
@@ -7,7 +7,7 @@
 {% block bodyid %}book-detail{% endblock %}
 
 {% block body %}
-    <h1>{{ book.title }}, {{ categories.author|join:", " }}</h1>
+    <h1>{% book_title book %}</h1>
     <form action="{% url search %}" method="get" accept-charset="utf-8" id="search-form">
         <p>{{ form.q }} <input type="submit" value="{% trans "Search" %}" /> <strong>{% trans "or" %}</strong> <a href="{% url main_page %}">{% trans "return to main page" %}</a></p>
     </form>
         <p>{% trans "Based on" %}: {{ extra_info.source_name }}</p>
         {% if book.has_description %}
             <div id="description">
-                {{ book.description|safe }}
+                <div id='description-long'>{{ book.description|safe }}</div>
+                <div id='description-short'>{{ book.description|safe|truncatewords_html:30 }}</div>
             </div>
-            <div id="toggle-description"><p>{% trans "Hide description" %} ▲</p></div>
+            <div id="toggle-description"><p></p></div>
         {% endif %}
         <div id="formats">
             <p class="change-sets">{% trans "Put a book" %} <span><a href="{% url catalogue.views.book_sets book.slug %}" class="jqm-trigger">{% trans "on the shelf!" %}</a></span></p>
             <h2>{% trans "Details" %}</h2>
             <ul>
                 <li>
+                       
                     {% trans "Author" %}:
                     {% for tag in categories.author %}
-                    <a href="{{ tag.get_absolute_url }}">{{ tag }}</a>
+                    <a href="{{ tag.get_absolute_url }}">{{ tag }}</a>{% if not forloop.last %}, {% endif %}
                     {% endfor %}
                 </li>
                 <li>
                     {% trans "Epoch" %}:
                     {% for tag in categories.epoch %}
-                    <a href="{{ tag.get_absolute_url }}">{{ tag }}</a>
+                    <a href="{{ tag.get_absolute_url }}">{{ tag }}</a>{% if not forloop.last %}, {% endif %}
                     {% endfor %}
                 </li>
                 <li>
                     {% trans "Kind" %}:
                     {% for tag in categories.kind %}
-                    <a href="{{ tag.get_absolute_url }}">{{ tag }}</a>
+                    <a href="{{ tag.get_absolute_url }}">{{ tag }}</a>{% if not forloop.last %}, {% endif %}
                     {% endfor %}
                 </li>
                 <li>
                     {% trans "Genre" %}:
                     {% for tag in categories.genre %}
-                    <a href="{{ tag.get_absolute_url }}">{{ tag }}</a>
+                    <a href="{{ tag.get_absolute_url }}">{{ tag }}</a>{% if not forloop.last %}, {% endif %}
                     {% endfor %}
                 </li>
             </ul>
index ccca721..4526857 100644 (file)
@@ -1,4 +1,5 @@
 {% load i18n %}
+{% load catalogue_tags %}
 <div class="fragment">
     {% if fragment.short_text %}
     <div class='fragment-short-text'>
@@ -13,7 +14,7 @@
         {% endif %}
     </div>
     <div class="fragment-metadata">
-        <p><a href="{{ book.get_absolute_url }}">{{ book.title }}</a>, {{ book_authors|join:"," }}
+        <p>{% book_title_html fragment.book %}
            <a href="{{ fragment.get_absolute_url }}">({% trans "See in a book" %})</a></p>
     </div>
     <div class="clearboth"></div>