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)
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
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))
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):
from django.test import TestCase
import shutil
import tempfile
+from slughifi import slughifi
class WLTestCase(TestCase):
"""
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,
+ }
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 *
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()
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()
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)
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 """
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 """
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):
'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')
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):
--- /dev/null
+# -*- 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')
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 = [
$(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;
}
}
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'}); },
{% 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>
{% load i18n %}
+{% load catalogue_tags %}
<div class="fragment">
{% if fragment.short_text %}
<div class='fragment-short-text'>
{% 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>