1 # This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
2 # Copyright © Fundacja Nowoczesna Polska. 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, SearchRank, 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
13 from .fields import JQueryAutoCompleteSearchField, InlineRadioWidget
14 from .utils import UnaccentSearchQuery, UnaccentSearchVector
17 class SearchForm(forms.Form):
18 q = JQueryAutoCompleteSearchField(label=_('Search'))
19 # {'minChars': 2, 'selectFirst': True, 'cacheLength': 50, 'matchContains': "word"})
21 def __init__(self, source, *args, **kwargs):
22 kwargs['auto_id'] = False
23 super(SearchForm, self).__init__(*args, **kwargs)
24 self.fields['q'].widget.attrs['id'] = 'search'
25 self.fields['q'].widget.attrs['autocomplete'] = 'off'
26 self.fields['q'].widget.attrs['data-source'] = source
27 if 'q' not in self.data:
28 self.fields['q'].widget.attrs['placeholder'] = _('title, author, epoch, kind, genre, phrase')
31 class SearchFilters(forms.Form):
33 required=False, widget=forms.HiddenInput(),
34 min_length=2, max_length=256,
36 format = forms.ChoiceField(required=False, choices=[
39 ('audio', 'audiobook'),
43 ], widget=InlineRadioWidget())
44 lang = forms.ChoiceField(required=False)
45 epoch = forms.ChoiceField(required=False)
46 genre = forms.ChoiceField(required=False)
47 category = forms.ChoiceField(required=False, choices=[
50 #('translator', 'tłumacz'),
55 ('collection', 'kolekcja'),
57 ], widget=InlineRadioWidget())
59 def __init__(self, *args, **kwargs):
60 super().__init__(*args, **kwargs)
62 langs = dict(settings.LANGUAGES)
63 self.fields['lang'].choices = [('', 'wszystkie')] + [
66 langs.get(LANGUAGES_3TO2.get(b, b), b)
68 for b in catalogue.models.Book.objects.values_list(
70 ).distinct().order_by()
72 self.fields['epoch'].choices = [('', 'wszystkie')] + [
74 for b in catalogue.models.Tag.objects.filter(category='epoch')
76 self.fields['genre'].choices = [('', 'wszystkie')] + [
78 for b in catalogue.models.Tag.objects.filter(category='genre')
81 def get_querysets(self):
83 'author': catalogue.models.Tag.objects.filter(category='author'),
84 'pdauthor': pdcounter.models.Author.objects.all(),
85 'theme': catalogue.models.Tag.objects.filter(category='theme'),
86 'genre': catalogue.models.Tag.objects.filter(category='genre'),
87 'collection': catalogue.models.Collection.objects.all(),
88 'book': catalogue.models.Book.objects.all(), #findable
89 'pdbook': pdcounter.models.BookStub.objects.all(),
90 'snippet': catalogue.models.Snippet.objects.all(),
91 'art': picture.models.Picture.objects.all(),
94 if self.cleaned_data['category']:
95 c = self.cleaned_data['category']
97 qs['author'] = qs['author'].none()
98 qs['pdauthor'] = qs['pdauthor'].none()
99 if c != 'theme': qs['theme'] = qs['theme'].none()
100 if c != 'genre': qs['genre'] = qs['genre'].none()
101 if c != 'collection': qs['collection'] = qs['collection'].none()
103 qs['book'] = qs['book'].none()
104 qs['pdbook'] = qs['pdbook'].none()
105 if c != 'quote': qs['snippet'] = qs['snippet'].none()
106 if c != 'art': qs['art'] = qs['art'].none()
107 qs['art'] = Picture.objects.none()
109 if self.cleaned_data['format']:
110 c = self.cleaned_data['format']
111 qs['author'] = qs['author'].none()
112 qs['pdauthor'] = qs['pdauthor'].none()
113 qs['theme'] = qs['theme'].none()
114 qs['genre'] = qs['genrer'].none()
115 qs['collection'] = qs['collection'].none()
117 qs['book'] = qs['book'].none()
118 qs['pdbook'] = qs['pdbook'].none()
119 qs['snippet'] = qs['snippet'].none()
120 if c in ('text', 'audio', 'daisy'):
121 qs['art'] = qs['art'].none()
123 qs['book'] = qs['book'].filter(media__type='mp3')
124 qs['pdbook'] = qs['book'].none()
125 qs['snippet'] = qs['snippet'].filter(book__media__type='mp3')
127 qs['book'] = qs['book'].filter(media__type='daisy')
128 qs['snippet'] = qs['snippet'].filter(book__media__type='daisy')
130 if self.cleaned_data['lang']:
131 qs['author'] = qs['author'].none()
132 qs['pdauthor'] = qs['pdauthor'].none()
133 qs['theme'] = qs['theme'].none()
134 qs['genre'] = qs['genre'].none()
135 qs['art'] = qs['art'].none()
136 qs['collection'] = qs['collection'].none()
137 qs['book'] = qs['book'].filter(language=self.cleaned_data['lang'])
138 qs['pdbook'] = qs['pdbook'].none()
139 qs['snippet'] = qs['snippet'].filter(book__language=self.cleaned_data['lang'])
141 for tag_cat in ('epoch', 'genre'):
142 c = self.cleaned_data[tag_cat]
145 t = catalogue.models.Tag.objects.get(category=tag_cat, slug=c)
146 qs['author'] = qs['author'].none()
147 qs['pdauthor'] = qs['pdauthor'].none()
148 qs['theme'] = qs['theme'].none()
149 qs['genre'] = qs['genre'].none()
150 qs['collection'] = qs['collection'].none()
151 qs['book'] = qs['book'].filter(tag_relations__tag=t)
152 qs['pdbook'] = qs['pdbook'].none()
153 qs['snippet'] = qs['snippet'].filter(book__tag_relations__tag=t)
154 qs['art'] = qs['art'].filter(tag_relations__tag=t)
159 qs = self.get_querysets()
160 query = self.cleaned_data['q']
161 squery = UnaccentSearchQuery(query, config='polish')
162 query = SearchQuery(query, config='polish')
163 books = qs['book'].annotate(
164 search_vector=UnaccentSearchVector('title')
165 ).filter(search_vector=squery)
166 books = books.exclude(ancestor__in=books)
168 snippets = qs['snippet'].annotate(
169 rank=SearchRank('search_vector', squery)
170 ).filter(rank__gt=0).order_by('-rank').annotate(
171 headline=SearchHeadline(
175 start_sel='<strong>',
176 stop_sel='</strong>',
180 snippets_by_book = {}
181 for snippet in snippets:
182 snippet_list = snippets_by_book.setdefault(snippet.book, [])
183 if len(snippet_list) < 3:
184 snippet_list.append(snippet)
187 'author': qs['author'].annotate(
188 search_vector=UnaccentSearchVector('name_pl')
189 ).filter(search_vector=squery),
190 'theme': qs['theme'].annotate(
191 search_vector=UnaccentSearchVector('name_pl')
192 ).filter(search_vector=squery),
193 'genre': qs['genre'].annotate(
194 search_vector=UnaccentSearchVector('name_pl')
195 ).filter(search_vector=squery),
196 'collection': qs['collection'].annotate(
197 search_vector=UnaccentSearchVector('title')
198 ).filter(search_vector=squery),
200 'art': qs['art'].annotate(
201 search_vector=UnaccentSearchVector('title')
202 ).filter(search_vector=squery)[:100],
203 'snippet': snippets_by_book,
204 'pdauthor': pdcounter.models.Author.search(squery, qs=qs['pdauthor']),
205 'pdbook': pdcounter.models.BookStub.search(squery, qs=qs['pdbook']),