+
+# ==========
+# = Search =
+# ==========
+
+def _no_diacritics_regexp(query):
+ """ returns a regexp for searching for a query without diacritics
+
+ should be locale-aware """
+ names = {'a':u'ą', 'c':u'ć', 'e':u'ę', 'l': u'ł', 'n':u'ń', 'o':u'ó', 's':u'ś', 'z':u'ź|ż'}
+ def repl(m):
+ l = m.group()
+ return "(%s|%s)" % (l, names[l])
+ return re.sub('[%s]'%(''.join(names.keys())), repl, query)
+
+def _word_starts_with(name, prefix):
+ """returns a Q object getting models having `name` contain a word
+ starting with `prefix`
+ """
+ kwargs = {}
+ if settings.DATABASE_ENGINE in ('mysql', 'postgresql_psycopg2', 'postgresql'):
+ prefix = _no_diacritics_regexp(re.escape(prefix))
+ # we could use a [[:<:]] (word start),
+ # but we want both `xy` and `(xy` to catch `(xyz)`
+ kwargs['%s__iregex' % name] = u"(^|[^[:alpha:]])%s" % prefix
+ else:
+ # don't know how to do a generic regex
+ # checking for simple icontain instead
+ kwargs['%s__icontains' % name] = prefix
+ return Q(**kwargs)
+
+
+def _tags_exact_matches(prefix, user):
+ book_stubs = models.BookStub.objects.filter(title__iexact = prefix)
+ books = models.Book.objects.filter(title__iexact = prefix)
+ book_stubs = filter(lambda x: x not in books, book_stubs)
+ tags = models.Tag.objects.filter(name__iexact = prefix)
+ if user.is_authenticated():
+ tags = tags.filter(~Q(category='book') & (~Q(category='set') | Q(user=user)))
+ else:
+ tags = tags.filter(~Q(category='book') & ~Q(category='set'))
+
+ return list(books) + list(tags) + list(book_stubs)
+
+
+def _tags_starting_with(prefix, user):
+ book_stubs = models.BookStub.objects.filter(_word_starts_with('title', prefix))
+ books = models.Book.objects.filter(_word_starts_with('title', prefix))
+ book_stubs = filter(lambda x: x not in books, book_stubs)
+ tags = models.Tag.objects.filter(_word_starts_with('name', prefix))
+ if user.is_authenticated():
+ tags = tags.filter(~Q(category='book') & (~Q(category='set') | Q(user=user)))
+ else:
+ tags = tags.filter(~Q(category='book') & ~Q(category='set'))
+
+ return list(books) + list(tags) + list(book_stubs)
+
+
+
+def _get_result_link(match, tag_list):
+ if isinstance(match, models.Book) or isinstance(match, models.BookStub):
+ return match.get_absolute_url()
+ else:
+ return reverse('catalogue.views.tagged_object_list',
+ kwargs={'tags': '/'.join(tag.slug for tag in tag_list + [match])}