1 # -*- coding: utf-8 -*-
3 from django.conf import settings
4 from django.shortcuts import render_to_response, get_object_or_404
5 from django.template import RequestContext
6 from django.views.decorators import cache
7 from django.http import HttpResponse, HttpResponseRedirect, Http404, HttpResponsePermanentRedirect
8 from django.utils.translation import ugettext as _
10 from catalogue.utils import split_tags
11 from catalogue.models import Book, Tag, Fragment
12 from pdcounter.models import Author as PDCounterAuthor, BookStub as PDCounterBook
13 from catalogue.views import JSONResponse
14 from search import Search, SearchResult
15 from suggest.forms import PublishingSuggestForm
21 def match_word_re(word):
22 if 'sqlite' in settings.DATABASES['default']['ENGINE']:
23 return r"\b%s\b" % word
24 elif 'mysql' in settings.DATABASES['default']['ENGINE']:
25 return "[[:<:]]%s[[:>:]]" % word
28 query_syntax_chars = re.compile(r"[\\/*:(){}]")
31 def remove_query_syntax_chars(query, replace=' '):
32 return query_syntax_chars.sub(' ', query)
35 def did_you_mean(query, tokens):
39 # authors = Tag.objects.filter(category='author', name__iregex=match_word_re(t))
40 # if len(authors) > 0:
44 # if not dictionary.check(t):
46 # change_to = dictionary.suggest(t)[0].lower()
47 # if change_to != t.lower():
48 # change[t] = change_to
55 # for frm, to in change.items():
56 # query = query.replace(frm, to)
62 prefix = request.GET.get('term', '')
64 return JSONResponse([])
66 prefix = remove_query_syntax_chars(prefix)
69 # tagi beda ograniczac tutaj
70 # ale tagi moga byc na ksiazce i na fragmentach
71 # jezeli tagi dot tylko ksiazki, to wazne zeby te nowe byly w tej samej ksiazce
72 # jesli zas dotycza themes, to wazne, zeby byly w tym samym fragmencie.
74 tags = search.hint_tags(prefix, pdcounter=True)
75 books = search.hint_books(prefix)
78 if isinstance(tag, PDCounterAuthor):
79 if filter(lambda t: t.slug == tag.slug and t != tag, tags):
81 elif isinstance(tag, PDCounterBook):
82 if filter(lambda b: b.slug == tag.slug, tags):
86 tags = filter(lambda t: not is_dupe(t), tags)
89 if c.startswith('pd_'):
93 callback = request.GET.get('callback', None)
94 data = [{'label': t.name,
95 'category': category_name(t.category),
97 'url': t.get_absolute_url()}
100 'category': _('book'),
102 'url': b.get_absolute_url()}
105 return HttpResponse("%s(%s);" % (callback, json.dumps(data)),
106 content_type="application/json; charset=utf-8")
108 return JSONResponse(data)
117 query = request.GET.get('q', '')
120 return render_to_response('catalogue/search_too_short.html',
122 context_instance=RequestContext(request))
124 query = remove_query_syntax_chars(query)
128 theme_terms = search.index.analyze(text=query, field="themes_pl") \
129 + search.index.analyze(text=query, field="themes")
132 tags = search.hint_tags(query, pdcounter=True, prefix=False)
133 tags = split_tags(tags)
135 author_results = search.search_phrase(query, 'authors', book=True)
136 translator_results = search.search_phrase(query, 'translators', book=True)
138 title_results = search.search_phrase(query, 'title', book=True)
140 # Boost main author/title results with mixed search, and save some of its results for end of list.
141 # boost author, title results
142 author_title_mixed = search.search_some(query, ['authors', 'translators', 'title', 'tags'], query_terms=theme_terms)
143 author_title_rest = []
145 for b in author_title_mixed:
146 also_in_mixed = filter(lambda ba: ba.book_id == b.book_id, author_results + translator_results + title_results)
147 for b2 in also_in_mixed:
149 if also_in_mixed is []:
150 author_title_rest.append(b)
152 # Do a phrase search but a term search as well - this can give us better snippets then search_everywhere,
153 # Because the query is using only one field.
154 text_phrase = SearchResult.aggregate(
155 search.search_phrase(query, 'text', snippets=True, book=False),
156 search.search_some(query, ['text'], snippets=True, book=False, query_terms=theme_terms))
158 everywhere = search.search_everywhere(query, query_terms=theme_terms)
160 def already_found(results):
163 if e.book_id == r.book_id:
169 f = already_found(author_results + translator_results + title_results + text_phrase)
170 everywhere = filter(lambda x: not f(x), everywhere)
172 author_results = SearchResult.aggregate(author_results)
173 translator_results = SearchResult.aggregate(translator_results)
174 title_results = SearchResult.aggregate(title_results)
176 everywhere = SearchResult.aggregate(everywhere, author_title_rest)
178 for field, res in [('authors', author_results),
179 ('translators', translator_results),
180 ('title', title_results),
181 ('text', text_phrase),
182 ('text', everywhere)]:
183 res.sort(reverse=True)
185 search.get_snippets(r, query, field, 3)
189 def ensure_exists(r):
192 except Book.DoesNotExist:
195 author_results = filter(ensure_exists, author_results)
196 translator_results = filter(ensure_exists, translator_results)
197 title_results = filter(ensure_exists, title_results)
198 text_phrase = filter(ensure_exists, text_phrase)
199 everywhere = filter(ensure_exists, everywhere)
201 results = author_results + translator_results + title_results + text_phrase + everywhere
202 # ensure books do exists & sort them
203 for res in (author_results, translator_results, title_results, text_phrase, everywhere):
204 res.sort(reverse=True)
206 # We don't want to redirect to book text, but rather display result page even with one result.
207 # if len(results) == 1:
208 # fragment_hits = filter(lambda h: 'fragment' in h, results[0].hits)
209 # if len(fragment_hits) == 1:
210 # #anchor = fragment_hits[0]['fragment']
211 # #frag = Fragment.objects.get(anchor=anchor)
212 # return HttpResponseRedirect(fragment_hits[0]['fragment'].get_absolute_url())
213 # return HttpResponseRedirect(results[0].book.get_absolute_url())
214 if len(results) == 0:
215 form = PublishingSuggestForm(initial={"books": query + ", "})
216 return render_to_response('catalogue/search_no_hits.html',
220 'did_you_mean': suggestion},
221 context_instance=RequestContext(request))
223 return render_to_response('catalogue/search_multiple_hits.html',
226 'results': {'author': author_results,
227 'translator': translator_results,
228 'title': title_results,
229 'content': text_phrase,
230 'other': everywhere},
231 'did_you_mean': suggestion},
232 context_instance=RequestContext(request))