1 # This file is part of Wolne Lektury, licensed under GNU Affero GPLv3 or later.
2 # Copyright © Fundacja Wolne Lektury. See NOTICE for more information.
4 from django.apps import apps
5 from django.conf import settings
6 from django.contrib.postgres.search import SearchHeadline, SearchQuery
7 from django import forms
8 from django.utils.translation import gettext_lazy as _
9 from catalogue.constants import LANGUAGES_3TO2
10 import catalogue.models
11 import pdcounter.models
12 from .fields import InlineRadioWidget
13 from .utils import UnaccentSearchQuery, UnaccentSearchVector
16 class SearchFilters(forms.Form):
18 required=False, widget=forms.HiddenInput(),
19 min_length=2, max_length=256,
21 format = forms.ChoiceField(required=False, choices=[
23 ('audio', _('audiobook')),
24 ('daisy', _('Daisy')),
25 ], widget=InlineRadioWidget())
26 lang = forms.ChoiceField(required=False)
27 epoch = forms.ChoiceField(required=False)
28 genre = forms.ChoiceField(required=False)
29 category = forms.ChoiceField(required=False, choices=[
31 ('author', _('autor')),
32 #('translator', _('tłumacz')),
33 ('theme', _('motyw')),
34 ('genre', _('gatunek')),
36 ('collection', _('kolekcja')),
37 ('quote', _('cytat')),
38 ], widget=InlineRadioWidget())
40 def __init__(self, *args, **kwargs):
41 super().__init__(*args, **kwargs)
43 langs = dict(settings.LANGUAGES)
44 self.fields['lang'].choices = [('', _('wszystkie'))] + [
47 langs.get(LANGUAGES_3TO2.get(b, b), b)
49 for b in catalogue.models.Book.objects.values_list(
51 ).distinct().order_by()
53 self.fields['epoch'].choices = [('', _('wszystkie'))] + [
55 for b in catalogue.models.Tag.objects.filter(category='epoch')
57 self.fields['genre'].choices = [('', _('wszystkie'))] + [
59 for b in catalogue.models.Tag.objects.filter(category='genre')
62 def get_querysets(self):
64 'author': catalogue.models.Tag.objects.filter(category='author'),
65 'pdauthor': pdcounter.models.Author.objects.all(),
66 'theme': catalogue.models.Tag.objects.filter(category='theme'),
67 'genre': catalogue.models.Tag.objects.filter(category='genre'),
68 'collection': catalogue.models.Collection.objects.all(),
69 'book': catalogue.models.Book.objects.filter(findable=True),
70 'pdbook': pdcounter.models.BookStub.objects.all(),
71 'snippet': catalogue.models.Snippet.objects.filter(book__findable=True),
73 if self.cleaned_data['category']:
74 c = self.cleaned_data['category']
76 qs['author'] = qs['author'].none()
77 qs['pdauthor'] = qs['pdauthor'].none()
78 if c != 'theme': qs['theme'] = qs['theme'].none()
79 if c != 'genre': qs['genre'] = qs['genre'].none()
80 if c != 'collection': qs['collection'] = qs['collection'].none()
82 qs['book'] = qs['book'].none()
83 qs['pdbook'] = qs['pdbook'].none()
84 if c != 'quote': qs['snippet'] = qs['snippet'].none()
86 if self.cleaned_data['format']:
87 c = self.cleaned_data['format']
88 qs['author'] = qs['author'].none()
89 qs['pdauthor'] = qs['pdauthor'].none()
90 qs['theme'] = qs['theme'].none()
91 qs['genre'] = qs['genre'].none()
92 qs['collection'] = qs['collection'].none()
94 qs['book'] = qs['book'].filter(media__type='mp3')
95 qs['pdbook'] = qs['book'].none()
96 qs['snippet'] = qs['snippet'].filter(book__media__type='mp3')
98 qs['book'] = qs['book'].filter(media__type='daisy')
99 qs['snippet'] = qs['snippet'].filter(book__media__type='daisy')
101 if self.cleaned_data['lang']:
102 qs['author'] = qs['author'].none()
103 qs['pdauthor'] = qs['pdauthor'].none()
104 qs['theme'] = qs['theme'].none()
105 qs['genre'] = qs['genre'].none()
106 qs['collection'] = qs['collection'].none()
107 qs['book'] = qs['book'].filter(language=self.cleaned_data['lang'])
108 qs['pdbook'] = qs['pdbook'].none()
109 qs['snippet'] = qs['snippet'].filter(book__language=self.cleaned_data['lang'])
111 for tag_cat in ('epoch', 'genre'):
112 c = self.cleaned_data[tag_cat]
115 t = catalogue.models.Tag.objects.get(category=tag_cat, slug=c)
116 qs['author'] = qs['author'].none()
117 qs['pdauthor'] = qs['pdauthor'].none()
118 qs['theme'] = qs['theme'].none()
119 qs['genre'] = qs['genre'].none()
120 qs['collection'] = qs['collection'].none()
121 qs['book'] = qs['book'].filter(tag_relations__tag=t)
122 qs['pdbook'] = qs['pdbook'].none()
123 qs['snippet'] = qs['snippet'].filter(book__tag_relations__tag=t)
128 qs = self.get_querysets()
129 query = self.cleaned_data['q']
130 squery = UnaccentSearchQuery(query, config=settings.SEARCH_CONFIG)
131 query = SearchQuery(query, config=settings.SEARCH_CONFIG)
132 books = qs['book'].annotate(
133 search_vector=UnaccentSearchVector('title')
134 ).filter(search_vector=squery)
135 books = books.exclude(ancestor__in=books).order_by('-popularity__count')
137 snippets = qs['snippet'].filter(search_vector=squery).annotate(
138 headline=SearchHeadline(
141 config=settings.SEARCH_CONFIG,
142 start_sel='<strong>',
143 stop_sel='</strong>',
145 ).order_by('-book__popularity__count', 'sec')[:100]
146 snippets_by_book = {}
147 for snippet in snippets:
148 snippet_list = snippets_by_book.setdefault(snippet.book, [])
149 if len(snippet_list) < 3:
150 snippet_list.append(snippet)
153 'author': qs['author'].annotate(
154 search_vector=UnaccentSearchVector('name_pl')
155 ).filter(search_vector=squery),
156 'theme': qs['theme'].annotate(
157 search_vector=UnaccentSearchVector('name_pl')
158 ).filter(search_vector=squery),
159 'genre': qs['genre'].annotate(
160 search_vector=UnaccentSearchVector('name_pl')
161 ).filter(search_vector=squery),
162 'collection': qs['collection'].annotate(
163 search_vector=UnaccentSearchVector('title')
164 ).filter(search_vector=squery),
166 'snippet': snippets_by_book,
167 'pdauthor': pdcounter.models.Author.search(squery, qs=qs['pdauthor']),
168 'pdbook': pdcounter.models.BookStub.search(squery, qs=qs['pdbook']),