From: Jan Szejko Date: Wed, 17 Feb 2016 13:26:27 +0000 (+0100) Subject: refactor tagged_object_list X-Git-Url: https://git.mdrn.pl/wolnelektury.git/commitdiff_plain/b2b4f8f39a4b15cbc3b9af7c06f75bbed9a84c88 refactor tagged_object_list --- diff --git a/src/catalogue/helpers.py b/src/catalogue/helpers.py index bb7fa6821..f74fad3e3 100644 --- a/src/catalogue/helpers.py +++ b/src/catalogue/helpers.py @@ -39,7 +39,6 @@ def get_top_level_related_tags(tags, categories=None): related = Tag.objects.filter(pk__in=related_ids) - # TODO: do we really need that? if categories is not None: related = related.filter(category__in=categories) diff --git a/src/catalogue/models/book.py b/src/catalogue/models/book.py index f954e1a90..676f9c028 100644 --- a/src/catalogue/models/book.py +++ b/src/catalogue/models/book.py @@ -89,7 +89,7 @@ class Book(models.Model): pass class Meta: - ordering = ('sort_key',) + ordering = ('sort_key_author', 'sort_key') verbose_name = _('book') verbose_name_plural = _('books') app_label = 'catalogue' diff --git a/src/catalogue/models/collection.py b/src/catalogue/models/collection.py index 0150055e0..15a4e2adc 100644 --- a/src/catalogue/models/collection.py +++ b/src/catalogue/models/collection.py @@ -49,7 +49,7 @@ class Collection(models.Model): def get_books(self): from catalogue.models import Book - return Book.objects.filter(self.get_query()).order_by('sort_key_author', 'sort_key') + return Book.objects.filter(self.get_query()) def flush_includes(self, languages=True): if not languages: diff --git a/src/catalogue/models/tag.py b/src/catalogue/models/tag.py index 06aa7a1d5..153197307 100644 --- a/src/catalogue/models/tag.py +++ b/src/catalogue/models/tag.py @@ -160,24 +160,24 @@ class Tag(TagBase): has_description.boolean = True @staticmethod - def get_tag_list(tags): - if isinstance(tags, basestring): - if not tags: + def get_tag_list(tag_str): + if isinstance(tag_str, basestring): + if not tag_str: return [] - real_tags = [] + tags = [] ambiguous_slugs = [] category = None deprecated = False - tags_splitted = tags.split('/') + tags_splitted = tag_str.split('/') for name in tags_splitted: if category: - real_tags.append(Tag.objects.get(slug=name, category=category)) + tags.append(Tag.objects.get(slug=name, category=category)) category = None elif name in Tag.categories_rev: category = Tag.categories_rev[name] else: try: - real_tags.append(Tag.objects.get(slug=name)) + tags.append(Tag.objects.get(slug=name)) deprecated = True except Tag.MultipleObjectsReturned: ambiguous_slugs.append(name) @@ -188,14 +188,14 @@ class Tag(TagBase): if ambiguous_slugs: # some tags should be qualified e = Tag.MultipleObjectsReturned() - e.tags = real_tags + e.tags = tags e.ambiguous_slugs = ambiguous_slugs raise e if deprecated: - raise Tag.UrlDeprecationWarning(tags=real_tags) - return real_tags + raise Tag.UrlDeprecationWarning(tags=tags) + return tags else: - return TagBase.get_tag_list(tags) + return TagBase.get_tag_list(tag_str) @property def url_chunk(self): diff --git a/src/catalogue/templates/catalogue/audiobook_list.html b/src/catalogue/templates/catalogue/audiobook_list.html deleted file mode 100644 index 7eda46e4b..000000000 --- a/src/catalogue/templates/catalogue/audiobook_list.html +++ /dev/null @@ -1,21 +0,0 @@ -{% extends "base.html" %} -{% load i18n %} -{% load catalogue_tags switch_tag social_tags %} -{% load ssi_include from ssify %} - -{% block titleextra %}{% trans "Audiobooks" %}{% endblock %} - -{% block bodyid %}audiobooks{% endblock %} - -{% block body %} -

{% trans "Audiobooks" %}

- - {% work_list best %} -

{% trans "Listing of all audiobooks" %}

- {% plain_list books by_author=True %} - -

{% trans "Listing of all DAISY files" %}

- {% plain_list daisy by_author=True %} - - -{% endblock %} diff --git a/src/catalogue/urls.py b/src/catalogue/urls.py index 016db4006..1d4051b27 100644 --- a/src/catalogue/urls.py +++ b/src/catalogue/urls.py @@ -43,12 +43,12 @@ urlpatterns += patterns( url(r'^rodzaj/$', 'tag_catalogue', {'category': 'kind'}, name='kind_catalogue'), url(r'^motyw/$', 'tag_catalogue', {'category': 'theme'}, name='theme_catalogue'), - url(r'^galeria/$', 'tagged_object_list', {'list_type': 'gallery'}, name='gallery'), + url(r'^galeria/$', 'gallery', name='gallery'), url(r'^kolekcje/$', 'collections', name='catalogue_collections'), - url(r'^lektury/$', 'tagged_object_list', name='book_list'), + url(r'^lektury/$', 'literature', name='book_list'), url(r'^lektury/(?P[a-zA-Z0-9-]+)/$', 'collection', name='collection'), - url(r'^audiobooki/$', 'tagged_object_list', {'list_type': 'audiobooks'}, name='audiobook_list'), + url(r'^audiobooki/$', 'audiobooks', name='audiobook_list'), url(r'^daisy/$', 'daisy_list', name='daisy_list'), url(r'^tags/$', 'tags_starting_with', name='hint'), url(r'^jtags/?$', 'json_tags_starting_with', name='jhint'), @@ -97,5 +97,6 @@ urlpatterns += patterns( name='tagged_object_list_gallery'), url(r'^audiobooki/(?P[a-zA-Z0-9-/]*)/$', 'tagged_object_list', {'list_type': 'audiobooks'}, name='tagged_object_list_audiobooks'), - url(r'^(?P[a-zA-Z0-9-/]*)/$', 'tagged_object_list', name='tagged_object_list'), + url(r'^(?P[a-zA-Z0-9-/]*)/$', 'tagged_object_list', {'list_type': 'books'}, + name='tagged_object_list'), ) diff --git a/src/catalogue/views.py b/src/catalogue/views.py index 12cfdc577..fc73c7137 100644 --- a/src/catalogue/views.py +++ b/src/catalogue/views.py @@ -35,20 +35,18 @@ staff_required = user_passes_test(lambda user: user.is_staff) def catalogue(request): return render(request, 'catalogue/catalogue.html', { - 'books': Book.objects.filter(parent=None).order_by('sort_key_author', 'sort_key'), - 'pictures': Picture.objects.order_by('sort_key_author', 'sort_key'), + 'books': Book.objects.filter(parent=None), + 'pictures': Picture.objects.all(), 'collections': Collection.objects.all(), 'active_menu_item': 'all_works', }) -def book_list(request, filter=None, get_filter=None, template_name='catalogue/book_list.html', +def book_list(request, filters=None, template_name='catalogue/book_list.html', nav_template_name='catalogue/snippets/book_list_nav.html', - list_template_name='catalogue/snippets/book_list.html', context=None): - """ generates a listing of all books, optionally filtered with a test function """ - if get_filter: - filter = get_filter() - books_by_author, orphans, books_by_parent = Book.book_list(filter) + list_template_name='catalogue/snippets/book_list.html'): + """ generates a listing of all books, optionally filtered """ + books_by_author, orphans, books_by_parent = Book.book_list(filters) books_nav = OrderedDict() for tag in books_by_author: if books_by_author[tag]: @@ -64,28 +62,8 @@ def book_list(request, filter=None, get_filter=None, template_name='catalogue/bo }, context_instance=RequestContext(request)) -def audiobook_list(request): - books = Book.objects.filter(media__type__in=('mp3', 'ogg')).distinct().order_by( - 'sort_key_author', 'sort_key') - books = list(books) - if len(books) > 3: - best = random.sample(books, 3) - else: - best = books - - daisy = Book.objects.filter(media__type='daisy').distinct().order_by('sort_key_author', 'sort_key') - - return render(request, 'catalogue/audiobook_list.html', { - 'books': books, - 'best': best, - 'daisy': daisy, - }) - - def daisy_list(request): - return book_list(request, Q(media__type='daisy'), - template_name='catalogue/daisy_list.html', - ) + return book_list(request, Q(media__type='daisy'), template_name='catalogue/daisy_list.html') def collection(request, slug): @@ -107,28 +85,97 @@ def differentiate_tags(request, tags, ambiguous_slugs): context_instance=RequestContext(request)) -# TODO: Rewrite this hellish piece of code which tries to do everything -def tagged_object_list(request, tags='', list_type='books'): - raw_tags = tags - # preliminary tests and conditions - gallery = list_type == 'gallery' - audiobooks = list_type == 'audiobooks' +def object_list(request, objects, fragments=None, related_tags=None, tags=None, list_type='books', extra=None): + if not tags: + tags = [] + tag_ids = [tag.pk for tag in tags] + + related_tag_lists = [] + if related_tags: + related_tag_lists.append(related_tags) + else: + related_tag_lists.append( + Tag.objects.usage_for_queryset(objects, counts=True).exclude(category='set').exclude(pk__in=tag_ids)) + if not (extra and extra.get('theme_is_set')): + if fragments is None: + if list_type == 'gallery': + fragments = PictureArea.objects.filter(picture__in=objects) + else: + fragments = Fragment.objects.filter(book__in=objects) + related_tag_lists.append( + Tag.objects.usage_for_queryset(fragments, counts=True).filter(category='theme').exclude(pk__in=tag_ids)) + + categories = split_tags(*related_tag_lists) + + objects = list(objects) + if len(objects) > 3: + best = random.sample(objects, 3) + else: + best = objects + + result = { + 'object_list': objects, + 'categories': categories, + 'list_type': list_type, + 'tags': tags, + + 'formats_form': forms.DownloadFormatsForm(), + 'best': best, + 'active_menu_item': list_type, + } + if extra: + result.update(extra) + return render_to_response( + 'catalogue/tagged_object_list.html', result, + context_instance=RequestContext(request)) + + +def literature(request): + books = Book.objects.filter(parent=None) + + last_published = Book.objects.exclude(cover_thumb='').filter(parent=None).order_by('-created_at')[:20] + most_popular = Book.objects.exclude(cover_thumb='')\ + .order_by('-popularity__count', 'sort_key_author', 'sort_key')[:20] + return object_list(request, books, related_tags=get_top_level_related_tags([]), extra={ + 'last_published': last_published, + 'most_popular': most_popular, + }) + + +def gallery(request): + return object_list(request, Picture.objects.all(), list_type='gallery') + + +def audiobooks(request): + audiobooks = Book.objects.filter(media__type__in=('mp3', 'ogg')).distinct() + return object_list(request, audiobooks, list_type='audiobooks', extra={ + 'daisy': Book.objects.filter(media__type='daisy').distinct(), + }) + + +class ResponseInstead(Exception): + def __init__(self, response): + super(ResponseInstead, self).__init__() + self.response = response + + +def analyse_tags(request, tag_str): try: - tags = Tag.get_tag_list(tags) + tags = Tag.get_tag_list(tag_str) except Tag.DoesNotExist: # Perhaps the user is asking about an author in Public Domain # counter (they are not represented in tags) - chunks = tags.split('/') + chunks = tag_str.split('/') if len(chunks) == 2 and chunks[0] == 'autor': - return pdcounter_views.author_detail(request, chunks[1]) + raise ResponseInstead(pdcounter_views.author_detail(request, chunks[1])) else: raise Http404 except Tag.MultipleObjectsReturned, e: # Ask the user to disambiguate - return differentiate_tags(request, e.tags, e.ambiguous_slugs) + raise ResponseInstead(differentiate_tags(request, e.tags, e.ambiguous_slugs)) except Tag.UrlDeprecationWarning, e: - return HttpResponsePermanentRedirect( - reverse('tagged_object_list', args=['/'.join(tag.url_chunk for tag in e.tags)])) + raise ResponseInstead(HttpResponsePermanentRedirect( + reverse('tagged_object_list', args=['/'.join(tag.url_chunk for tag in e.tags)]))) try: if len(tags) > settings.MAX_TAG_LIST: @@ -136,130 +183,72 @@ def tagged_object_list(request, tags='', list_type='books'): except AttributeError: pass - # beginning of digestion - theme_is_set = any(tag.category == 'theme' for tag in tags) - shelf_is_set = any(tag.category == 'set' for tag in tags) - only_shelf = shelf_is_set and len(tags) == 1 - only_my_shelf = only_shelf and request.user == tags[0].user - tags_pks = [tag.pk for tag in tags] - - if gallery and shelf_is_set: - raise Http404 - - daisy = None - if theme_is_set: - # Only fragments (or pirctureareas) here. - shelf_tags = [tag for tag in tags if tag.category == 'set'] - fragment_tags = [tag for tag in tags if tag.category != 'set'] - if gallery: - fragments = PictureArea.tagged.with_all(fragment_tags) - else: - fragments = Fragment.tagged.with_all(fragment_tags) - - if shelf_tags: - # TODO: Pictures on shelves not supported yet. - books = Book.tagged.with_all(shelf_tags).order_by() - fragments = fragments.filter(Q(book__in=books) | Q(book__ancestor__in=books)) + return tags - categories = split_tags( - Tag.objects.usage_for_queryset(fragments, counts=True).exclude(pk__in=tags_pks), - ) - objects = fragments +def theme_list(request, tags, list_type): + shelf_tags = [tag for tag in tags if tag.category == 'set'] + fragment_tags = [tag for tag in tags if tag.category != 'set'] + if list_type == 'gallery': + fragments = PictureArea.tagged.with_all(fragment_tags) else: - if gallery: - # TODO: Pictures on shelves not supported yet. - if tags: - objects = Picture.tagged.with_all(tags) - else: - objects = Picture.objects.all() - areas = PictureArea.objects.filter(picture__in=objects) - categories = split_tags( - Tag.objects.usage_for_queryset( - objects, counts=True).exclude(pk__in=tags_pks), - Tag.objects.usage_for_queryset( - areas, counts=True).filter( - category__in=('theme', 'thing')).exclude( - pk__in=tags_pks), - ) - else: - if audiobooks: - all_books = Book.objects.filter(media__type__in=('mp3', 'ogg')).distinct() - if tags: - all_books = Book.tagged.with_all(tags, all_books) - objects = all_books - # there's never only the daisy audiobook - daisy = objects.filter(media__type='daisy').distinct().order_by('sort_key_author', 'sort_key') - related_book_tags = Tag.objects.usage_for_queryset( - objects, counts=True).exclude( - category='set').exclude(pk__in=tags_pks) - else: - if tags: - all_books = Book.tagged.with_all(tags) - else: - all_books = Book.objects.filter(parent=None) - if shelf_is_set: - objects = all_books - related_book_tags = Tag.objects.usage_for_queryset( - objects, counts=True).exclude( - category='set').exclude(pk__in=tags_pks) - else: - if tags: - objects = Book.tagged_top_level(tags) - else: - objects = all_books - related_book_tags = get_top_level_related_tags(tags) - - fragments = Fragment.objects.filter(book__in=all_books) - - categories = split_tags( - related_book_tags, - Tag.objects.usage_for_queryset( - fragments, counts=True).filter( - category='theme').exclude(pk__in=tags_pks), - ) - objects = objects.order_by('sort_key_author', 'sort_key') + fragments = Fragment.tagged.with_all(fragment_tags) - objects = list(objects) - if len(objects) > 3: - best = random.sample(objects, 3) - else: - best = objects + if shelf_tags: + # TODO: Pictures on shelves not supported yet. + books = Book.tagged.with_all(shelf_tags).order_by() + fragments = fragments.filter(Q(book__in=books) | Q(book__ancestor__in=books)) - if not gallery and not objects and len(tags) == 1: + if not fragments and len(tags) == 1: tag = tags[0] - if tag.category in ('theme', 'thing') and ( + if tag.category == 'theme' and ( PictureArea.tagged.with_any([tag]).exists() or Picture.tagged.with_any([tag]).exists()): - return redirect('tagged_object_list_gallery', raw_tags) + return redirect('tagged_object_list_gallery', '/'.join(tag.url_chunk for tag in tags)) + + return object_list(request, fragments, tags=tags, list_type=list_type, extra={ + 'theme_is_set': True, + 'active_menu_item': 'theme', + }) + + +def tagged_object_list(request, tags, list_type): + try: + tags = analyse_tags(request, tags) + except ResponseInstead as e: + return e.response + + if list_type == 'gallery' and any(tag.category == 'set' for tag in tags): + raise Http404 - # this is becoming more and more hacky - if list_type == 'books' and not tags: - last_published = Book.objects.exclude(cover_thumb='').filter(parent=None).order_by('-created_at')[:20] - most_popular = Book.objects.exclude(cover_thumb='').order_by('-popularity__count')[:20] + if any(tag.category == 'theme' for tag in tags): + return theme_list(request, tags, list_type=list_type) + + if list_type == 'books': + books = Book.tagged.with_all(tags) + + if any(tag.category == 'set' for tag in tags): + params = {'objects': books} + else: + params = { + 'objects': Book.tagged_top_level(tags), + 'fragments': Fragment.objects.filter(book__in=books), + 'related_tags': get_top_level_related_tags(tags), + } + elif list_type == 'gallery': + params = {'objects': Picture.tagged.with_all(tags)} + elif list_type == 'audiobooks': + audiobooks = Book.objects.filter(media__type__in=('mp3', 'ogg')).distinct() + params = { + 'objects': Book.tagged.with_all(tags, audiobooks), + 'extra': { + 'daisy': Book.tagged.with_all(tags, audiobooks.filter(media__type='daisy').distinct()), + } + } else: - last_published = None - most_popular = None + raise Http404 - return render_to_response( - 'catalogue/tagged_object_list.html', - { - 'object_list': objects, - 'categories': categories, - 'only_shelf': only_shelf, - 'only_my_shelf': only_my_shelf, - 'formats_form': forms.DownloadFormatsForm(), - 'tags': tags, - 'tag_ids': tags_pks, - 'theme_is_set': theme_is_set, - 'best': best, - 'list_type': list_type, - 'daisy': daisy, - 'last_published': last_published, - 'most_popular': most_popular, - 'active_menu_item': 'theme' if theme_is_set else list_type, - }, - context_instance=RequestContext(request)) + return object_list(request, tags=tags, list_type=list_type, **params) def book_fragments(request, slug, theme_slug): diff --git a/src/newtagging/views.py b/src/newtagging/views.py index e94680c0c..12e528a3b 100644 --- a/src/newtagging/views.py +++ b/src/newtagging/views.py @@ -7,7 +7,7 @@ from django.utils.translation import ugettext as _ from django.views.generic import ListView -def tagged_object_list(request, queryset_or_model=None, tag_model=None, tags=None, related_tags=False, +def tagged_object_list(request, queryset_or_model, tag_model, tags, related_tags=False, related_tag_counts=True, **kwargs): """ A thin wrapper around @@ -25,13 +25,6 @@ def tagged_object_list(request, queryset_or_model=None, tag_model=None, tags=Non tag will have a ``count`` attribute indicating the number of items which have it in addition to the given tag. """ - # Check attributes - if queryset_or_model is None: - raise AttributeError(_('tagged_object_list must be called with a queryset or a model.')) - if tag_model is None: - raise AttributeError(_('tagged_object_list must be called with a tag model.')) - if tags is None: - raise AttributeError(_('tagged_object_list must be called with a tag.')) tag_instances = tag_model.get_tag_list(tags) if tag_instances is None: diff --git a/src/picture/models.py b/src/picture/models.py index 5bc8d49d9..dc7142c0a 100644 --- a/src/picture/models.py +++ b/src/picture/models.py @@ -101,7 +101,7 @@ class Picture(models.Model): pass class Meta: - ordering = ('sort_key',) + ordering = ('sort_key_author', 'sort_key') verbose_name = _('picture') verbose_name_plural = _('pictures') diff --git a/src/picture/templates/picture/picture_list_thumb.html b/src/picture/templates/picture/picture_list_thumb.html index adfc25eb5..a38efa517 100644 --- a/src/picture/templates/picture/picture_list_thumb.html +++ b/src/picture/templates/picture/picture_list_thumb.html @@ -20,11 +20,6 @@
- - -{% work_list book_list %} - - - +{% work_list picture_list %} {% endblock %} diff --git a/src/picture/views.py b/src/picture/views.py index c0be52e2d..b555056b8 100644 --- a/src/picture/views.py +++ b/src/picture/views.py @@ -32,14 +32,12 @@ from wolnelektury.utils import ajax def picture_list_thumb(request, filter=None, get_filter=None, template_name='picture/picture_list_thumb.html', cache_key=None, context=None): - book_list = Picture.objects.all() + pictures = Picture.objects.all() if filter: - book_list = book_list.filter(filter) + pictures = pictures.filter(filter) if get_filter: - book_list = book_list.filter(get_filter()) - book_list = book_list.order_by('sort_key_author') - book_list = list(book_list) - return render_to_response(template_name, {'book_list': book_list}, context_instance=RequestContext(request)) + pictures = pictures.filter(get_filter()) + return render_to_response(template_name, {'book_list': list(pictures)}, context_instance=RequestContext(request)) def picture_detail(request, slug):