d6edd51641a3584bf31d462ca77d7d7720da0e47
[wolnelektury.git] / src / search / views.py
1 # This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
2 # Copyright © Fundacja Nowoczesna Polska. 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 picture.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 @cache.never_cache
28 def hint(request, mozhint=False, param='term'):
29     prefix = request.GET.get(param, '')
30     if len(prefix) < 2:
31         return JsonResponse([], safe=False)
32
33     prefix = re_escape(' '.join(remove_query_syntax_chars(prefix).split()))
34
35     try:
36         limit = int(request.GET.get('max', ''))
37     except ValueError:
38         limit = 20
39     else:
40         if limit < 1:
41             limit = 20
42
43     data = []
44     if len(data) < limit:
45         authors = catalogue.models.Tag.objects.filter(
46             category='author', name_pl__iregex='\m' + prefix).only('name', 'id', 'slug', 'category')
47         data.extend([
48             {
49                 'type': 'author',
50                 'label': author.name,
51                 'url': author.get_absolute_gallery_url() if author.for_pictures else author.get_absolute_url(),
52                 'img': get_thumbnail(author.photo, '72x72', crop='top').url if author.photo else '',
53             }
54             for author in authors[:limit - len(data)]
55         ])
56     if request.user.is_authenticated and len(data) < limit:
57         tags = catalogue.models.Tag.objects.filter(
58             category='set', user=request.user, name_pl__iregex='\m' + prefix).only('name', 'id', 'slug', 'category')
59         data.extend([
60             {
61                 'type': 'set',
62                 'label': tag.name,
63                 'url': tag.get_absolute_url(),
64             }
65             for tag in tags[:limit - len(data)]
66         ])
67     if len(data) < limit:
68         tags = catalogue.models.Tag.objects.filter(
69             category__in=('theme', 'genre', 'epoch', 'kind'), name_pl__iregex='\m' + prefix).only('name', 'id', 'slug', 'category')
70         data.extend([
71             {
72                 'type': tag.category,
73                 'label': tag.name,
74                 'url': tag.get_absolute_gallery_url() if tag.for_pictures else tag.get_absolute_url(),
75             }
76             for tag in tags[:limit - len(data)]
77         ])
78     if len(data) < limit:
79         collections = catalogue.models.Collection.objects.filter(
80             title_pl__iregex='\m' + prefix).only('title', 'slug')
81         data.extend([
82             {
83                 'type': 'collection',
84                 'label': collection.title,
85                 'url': collection.get_absolute_url(),
86             }
87             for collection in collections[:limit - len(data)]
88         ])
89     if len(data) < limit:
90         for b in catalogue.models.Book.objects.filter(findable=True, title__iregex='\m' + prefix)[:limit-len(data)]:
91             author_str = b.author_unicode()
92             translator = b.translator()
93             if translator:
94                 author_str += ' (tłum. ' + translator + ')'
95             data.append(
96                 {
97                     'type': 'book',
98                     'label': b.title,
99                     'author': author_str,
100                     'url': b.get_absolute_url(),
101                     'img': get_thumbnail(b.cover_clean, '72x72').url if b.cover_clean else '',
102                 }
103             )
104     if len(data) < limit:
105         arts = picture.models.Picture.objects.filter(
106             title__iregex='\m' + prefix).only('title', 'id', 'slug') # img?
107         data.extend([
108             {
109                 'type': 'art',
110                 'label': art.title,
111                 'author': art.author_unicode(),
112                 'url': art.get_absolute_url(),
113                 'img': get_thumbnail(art.image_file, '72x72').url if art.image_file else '',
114             }
115             for art in arts[:limit - len(data)]
116         ])
117     if len(data) < limit:
118         infos = infopages.models.InfoPage.objects.filter(
119             title_pl__iregex='\m' + prefix).only('title', 'id', 'slug')
120         data.extend([
121             {
122                 'type': 'info',
123                 'label': info.title,
124                 'url': info.get_absolute_url(),
125             }
126             for info in infos[:limit - len(data)]
127         ])
128
129     if mozhint:
130         data = [
131             prefix,
132             [
133                 item['label']
134                 for item in data
135             ],
136             [
137                 item.get('author', '')
138                 for item in data
139             ],
140             [
141                 item['url']
142                 for item in data
143             ]
144         ]
145
146     callback = request.GET.get('callback', None)
147     if callback:
148         return HttpResponse("%s(%s);" % (callback, json.dumps(data)),
149                             content_type="application/json; charset=utf-8")
150     else:
151         return JsonResponse(data, safe=False)
152
153
154
155 @cache.never_cache
156 def search(request):
157     filters = SearchFilters(request.GET)
158     ctx = {
159         'title': 'Wynik wyszukiwania',
160         'query': request.GET.get('q', ''),
161         'filters': filters,
162     }
163     if filters.is_valid():
164         ctx['results'] = filters.results()
165         for k, v in ctx['results'].items():
166             if v:
167                 ctx['hasresults'] = True
168                 break
169     return render(request, 'search/results.html', ctx)