fix
[wolnelektury.git] / src / search / views.py
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.
3 #
4 from django.conf import settings
5 from django.shortcuts import render
6 from django.views.decorators import cache
7 from django.http import HttpResponse, JsonResponse
8 from sorl.thumbnail import get_thumbnail
9
10 import catalogue.models
11 import infopages.models
12 import social.models
13 from .forms import SearchFilters
14 import re
15 import json
16
17 from wolnelektury.utils import re_escape
18
19
20 query_syntax_chars = re.compile(r"[\\/*:(){}?.[\]+]")
21
22
23 def remove_query_syntax_chars(query, replace=' '):
24     return query_syntax_chars.sub(replace, query)
25
26
27 def get_hints(prefix, user=None, limit=10):
28     if not prefix: return []
29     data = []
30     if len(data) < limit:
31         authors = catalogue.models.Tag.objects.filter(
32             category='author', name_pl__iregex='\m' + prefix).only('name', 'id', 'slug', 'category')
33         data.extend([
34             {
35                 'type': 'author',
36                 'label': author.name,
37                 'url': author.get_absolute_url(),
38                 'img': get_thumbnail(author.photo, '72x72', crop='top').url if author.photo else '',
39                 'slug': author.slug,
40             }
41             for author in authors[:limit - len(data)]
42         ])
43     
44     if user is not None and user.is_authenticated and len(data) < limit:
45         tags = social.models.UserList.objects.filter(
46             user=user, name__iregex='\m' + prefix).only('name', 'id', 'slug')
47         data.extend([
48             {
49                 'type': 'userlist',
50                 'label': tag.name,
51                 'url': tag.get_absolute_url(),
52                 'slug': tag.slug,
53             }
54             for tag in tags[:limit - len(data)]
55         ])
56     if len(data) < limit:
57         tags = catalogue.models.Tag.objects.filter(
58             category__in=('theme', 'genre', 'epoch', 'kind'), name_pl__iregex='\m' + prefix).only('name', 'id', 'slug', 'category')
59         data.extend([
60             {
61                 'type': tag.category,
62                 'label': tag.name,
63                 'url': tag.get_absolute_url(),
64                 'slug': tag.slug,
65             }
66             for tag in tags[:limit - len(data)]
67         ])
68     if len(data) < limit:
69         collections = catalogue.models.Collection.objects.filter(
70             title_pl__iregex='\m' + prefix).only('title', 'slug')
71         data.extend([
72             {
73                 'type': 'collection',
74                 'label': collection.title,
75                 'url': collection.get_absolute_url(),
76                 'slug': collection.slug,
77             }
78             for collection in collections[:limit - len(data)]
79         ])
80     if len(data) < limit:
81         for b in catalogue.models.Book.objects.filter(findable=True, title__iregex='\m' + prefix)[:limit-len(data)]:
82             author_str = b.author_unicode()
83             translator = b.translator()
84             if translator:
85                 author_str += ' (tłum. ' + translator + ')'
86             data.append(
87                 {
88                     'type': 'book',
89                     'label': b.title,
90                     'author': author_str,
91                     'url': b.get_absolute_url(),
92                     'img': get_thumbnail(b.cover_clean, '72x72').url if b.cover_clean else '',
93                     'slug': b.slug,
94                 }
95             )
96     if len(data) < limit:
97         infos = infopages.models.InfoPage.objects.filter(
98             published=True,
99             findable=True,
100             title_pl__iregex='\m' + prefix).only('title', 'id', 'slug')
101         data.extend([
102             {
103                 'type': 'info',
104                 'label': info.title,
105                 'url': info.get_absolute_url(),
106                 'slug': info.slug,
107             }
108             for info in infos[:limit - len(data)]
109         ])
110     return data
111
112
113 @cache.never_cache
114 def hint(request, mozhint=False, param='term'):
115     prefix = request.GET.get(param, '')
116     if len(prefix) < 2:
117         return JsonResponse([], safe=False)
118
119     prefix = re_escape(' '.join(remove_query_syntax_chars(prefix).split()))
120
121     try:
122         limit = int(request.GET.get('max', ''))
123     except ValueError:
124         limit = 20
125     else:
126         if limit < 1:
127             limit = 20
128
129     data = get_hints(
130         prefix,
131         user=request.user if request.user.is_authenticated else None,
132         limit=limit
133     )
134
135     if mozhint:
136         data = [
137             prefix,
138             [
139                 item['label']
140                 for item in data
141             ],
142             [
143                 item.get('author', '')
144                 for item in data
145             ],
146             [
147                 item['url']
148                 for item in data
149             ]
150         ]
151
152     callback = request.GET.get('callback', None)
153     if callback:
154         return HttpResponse("%s(%s);" % (callback, json.dumps(data)),
155                             content_type="application/json; charset=utf-8")
156     else:
157         return JsonResponse(data, safe=False)
158
159
160
161 @cache.never_cache
162 def search(request):
163     filters = SearchFilters(request.GET)
164     ctx = {
165         'title': 'Wynik wyszukiwania',
166         'query': request.GET.get('q', ''),
167         'filters': filters,
168     }
169     if filters.is_valid():
170         ctx['results'] = filters.results()
171         for k, v in ctx['results'].items():
172             if v:
173                 ctx['hasresults'] = True
174                 break
175     return render(request, 'search/results.html', ctx)