342 строки
9.8 KiB
Python
342 строки
9.8 KiB
Python
from collections import defaultdict
|
|
|
|
import commonware.log
|
|
import jingo
|
|
from tower import ugettext as _
|
|
from mobility.decorators import mobile_template
|
|
|
|
import amo
|
|
import bandwagon.views
|
|
import browse.views
|
|
from amo.decorators import json_view
|
|
from amo.helpers import urlparams
|
|
from amo.utils import MenuItem
|
|
from versions.compare import dict_from_int, version_int
|
|
from search import forms
|
|
from search.client import (Client as SearchClient, SearchError,
|
|
CollectionsClient, PersonasClient, sphinx)
|
|
from search.forms import SearchForm, SecondarySearchForm
|
|
|
|
DEFAULT_NUM_RESULTS = 20
|
|
|
|
log = commonware.log.getLogger('z.search')
|
|
|
|
|
|
def _get_versions(request, versions, version):
|
|
compats = []
|
|
url = request.get_full_path()
|
|
|
|
c = MenuItem()
|
|
(c.text, c.url) = (_('All Versions'), urlparams(url, lver=None, page=None))
|
|
|
|
if not version or version == 'any':
|
|
c.selected = True
|
|
|
|
compats.append(c)
|
|
seen = {}
|
|
exclude = request.APP.__dict__.get('exclude_versions', [])
|
|
versions.sort(reverse=True)
|
|
|
|
for v in versions:
|
|
# v is a version_int so we can get the major and minor:
|
|
v = dict_from_int(v)
|
|
v_float = v['major'] + v['minor1'] / 10.0
|
|
text = "%0.1f" % v_float
|
|
|
|
if seen.get(text):
|
|
continue
|
|
|
|
seen[text] = 1
|
|
|
|
if v_float < request.APP.min_display_version or v_float in exclude:
|
|
continue
|
|
|
|
c = MenuItem()
|
|
c.text = text
|
|
c.url = urlparams(url, lver=c.text, page=None)
|
|
|
|
if c.text == version:
|
|
c.selected = True
|
|
compats.append(c)
|
|
|
|
return compats
|
|
|
|
|
|
def _get_categories(request, categories, addon_type=None, category=None):
|
|
items = []
|
|
url = request.get_full_path()
|
|
|
|
i = MenuItem()
|
|
(i.text, i.url) = (_('All'), urlparams(url, atype=None, cat=None,
|
|
page=None))
|
|
|
|
if not addon_type and not category:
|
|
i.selected = True
|
|
|
|
items.append(i)
|
|
|
|
# Bucket the categories as addon_types so we can display them in a
|
|
# hierarchy.
|
|
bucket = defaultdict(list)
|
|
|
|
for cat in categories:
|
|
item = MenuItem()
|
|
(item.text, item.url) = (cat.name, urlparams(url, atype=None,
|
|
page=None, cat="%d,%d" % (cat.type, cat.id)))
|
|
|
|
if category == cat.id:
|
|
item.selected = True
|
|
|
|
bucket[cat.type].append(item)
|
|
|
|
for key in sorted(bucket):
|
|
children = bucket[key]
|
|
item = MenuItem()
|
|
item.children = children
|
|
(item.text, item.url) = (amo.ADDON_TYPES[key],
|
|
urlparams(url, atype=key, cat=None,
|
|
page=None))
|
|
if not category and addon_type == key:
|
|
item.selected = True
|
|
|
|
items.append(item)
|
|
|
|
return items
|
|
|
|
|
|
def _get_platforms(request, platforms, selected=None):
|
|
items = []
|
|
url = request.get_full_path()
|
|
|
|
if amo.PLATFORM_ALL.id in platforms:
|
|
platforms = amo.PLATFORMS.keys()
|
|
|
|
for platform in platforms:
|
|
if platform == amo.PLATFORM_ALL.id:
|
|
continue
|
|
item = MenuItem()
|
|
p = amo.PLATFORMS[platform]
|
|
(item.text, item.url) = (p.name,
|
|
urlparams(url, pid=(p.id or None), page=None))
|
|
if p.id == selected:
|
|
item.selected = True
|
|
items.append(item)
|
|
|
|
return items
|
|
|
|
|
|
def _get_tags(request, tags, selected):
|
|
items = []
|
|
url = request.get_full_path()
|
|
|
|
for tag in tags:
|
|
item = MenuItem()
|
|
(item.text, item.url) = (tag.tag_text.lower(),
|
|
urlparams(url, tag=tag.tag_text.encode('utf8').lower(),
|
|
page=None))
|
|
|
|
if tag.tag_text.lower() == selected:
|
|
item.selected = True
|
|
|
|
items.append(item)
|
|
|
|
return items
|
|
|
|
|
|
def _get_sort_menu(request, sort):
|
|
items = []
|
|
url = request.get_full_path()
|
|
sorts = forms.sort_by
|
|
|
|
item = (None, _('Keyword Match'))
|
|
items.append(item)
|
|
|
|
for key, val in sorts:
|
|
if key == '':
|
|
continue
|
|
item = (key, val)
|
|
items.append(item)
|
|
|
|
return items
|
|
|
|
|
|
def _get_sorts(request, sort):
|
|
items = []
|
|
url = request.get_full_path()
|
|
|
|
sorts = forms.sort_by
|
|
|
|
item = MenuItem()
|
|
(item.text, item.url) = (_('Keyword Match'), urlparams(url, sort=None))
|
|
|
|
if not sort:
|
|
item.selected = True
|
|
|
|
items.append(item)
|
|
|
|
for key, val in sorts:
|
|
if key == '':
|
|
continue
|
|
|
|
item = MenuItem()
|
|
(item.text, item.url) = (val, urlparams(url, sort=key, page=None))
|
|
|
|
if sort == key:
|
|
item.selected = True
|
|
|
|
items.append(item)
|
|
|
|
return items
|
|
|
|
|
|
def _personas(request):
|
|
"""Handle the request for persona searches."""
|
|
form = SecondarySearchForm(request.GET)
|
|
if not form.is_valid():
|
|
log.error(form.errors)
|
|
|
|
query = form.data.get('q', '')
|
|
|
|
search_opts = {}
|
|
search_opts['limit'] = form.cleaned_data.get('pp', DEFAULT_NUM_RESULTS)
|
|
page = form.cleaned_data.get('page') or 1
|
|
search_opts['offset'] = (page - 1) * search_opts['limit']
|
|
|
|
try:
|
|
results = PersonasClient().query(query, **search_opts)
|
|
except SearchError:
|
|
return jingo.render(request, 'search/down.html', {}, status=503)
|
|
|
|
pager = amo.utils.paginate(request, results, search_opts['limit'])
|
|
categories, filter, _, _ = browse.views.personas_listing(request)
|
|
c = dict(pager=pager, form=form, categories=categories, query=query,
|
|
filter=filter)
|
|
return jingo.render(request, 'search/personas.html', c)
|
|
|
|
|
|
def _collections(request):
|
|
"""Handle the request for collections."""
|
|
form = SecondarySearchForm(request.GET)
|
|
form.is_valid()
|
|
|
|
query = form.cleaned_data.get('q', '')
|
|
|
|
search_opts = {}
|
|
search_opts['limit'] = form.cleaned_data.get('pp', DEFAULT_NUM_RESULTS)
|
|
page = form.cleaned_data.get('page') or 1
|
|
search_opts['offset'] = (page - 1) * search_opts['limit']
|
|
search_opts['sort'] = form.cleaned_data.get('sortby')
|
|
|
|
try:
|
|
results = CollectionsClient().query(query, **search_opts)
|
|
except SearchError:
|
|
return jingo.render(request, 'search/down.html', {}, status=503)
|
|
|
|
pager = amo.utils.paginate(request, results, search_opts['limit'])
|
|
c = dict(pager=pager, form=form, query=query, opts=search_opts,
|
|
filter=bandwagon.views.get_filter(request))
|
|
return jingo.render(request, 'search/collections.html', c)
|
|
|
|
|
|
@json_view
|
|
def ajax_search(request):
|
|
""" Returns a json feed of ten results for auto-complete used in
|
|
collections.
|
|
[
|
|
{"id": 123, "name": "best addon", "icon": "http://path/to/icon"},
|
|
...
|
|
]
|
|
"""
|
|
|
|
q = request.GET.get('q', '')
|
|
client = SearchClient()
|
|
try:
|
|
|
|
results = client.query('@name ' + q, limit=10,
|
|
match=sphinx.SPH_MATCH_EXTENDED2)
|
|
return [dict(id=result.id, label=unicode(result.name),
|
|
icon=result.icon_url, value=unicode(result.name).lower())
|
|
for result in results]
|
|
except SearchError:
|
|
return []
|
|
|
|
|
|
@mobile_template('search/{mobile/}results.html')
|
|
def search(request, tag_name=None, template=None):
|
|
# If the form is invalid we still want to have a query.
|
|
query = request.REQUEST.get('q', '')
|
|
|
|
search_opts = {
|
|
'meta': ('versions', 'categories', 'tags', 'platforms'),
|
|
'version': None,
|
|
}
|
|
|
|
form = SearchForm(request)
|
|
form.is_valid() # Let the form try to clean data.
|
|
|
|
category = form.cleaned_data.get('cat')
|
|
|
|
if category == 'collections':
|
|
return _collections(request)
|
|
elif category == 'personas':
|
|
return _personas(request)
|
|
|
|
# TODO: Let's change the form values to something less gross when
|
|
# Remora dies in a fire.
|
|
query = form.cleaned_data['q']
|
|
|
|
addon_type = form.cleaned_data.get('atype', 0)
|
|
tag = tag_name if tag_name is not None else form.cleaned_data.get('tag')
|
|
if tag_name:
|
|
search_opts['show_personas'] = True
|
|
page = form.cleaned_data['page']
|
|
sort = form.cleaned_data.get('sort')
|
|
|
|
search_opts['version'] = form.cleaned_data.get('lver')
|
|
search_opts['limit'] = form.cleaned_data.get('pp', DEFAULT_NUM_RESULTS)
|
|
search_opts['platform'] = form.cleaned_data.get('pid', amo.PLATFORM_ALL)
|
|
search_opts['sort'] = sort
|
|
search_opts['app'] = request.APP.id
|
|
search_opts['offset'] = (page - 1) * search_opts['limit']
|
|
|
|
if category:
|
|
search_opts['category'] = category
|
|
elif addon_type:
|
|
search_opts['type'] = addon_type
|
|
|
|
search_opts['tag'] = tag
|
|
|
|
client = SearchClient()
|
|
|
|
try:
|
|
results = client.query(query, **search_opts)
|
|
except SearchError, e:
|
|
log.error('Sphinx Error: %s' % e)
|
|
return jingo.render(request, 'search/down.html', locals(), status=503)
|
|
|
|
version_filters = client.meta['versions']
|
|
|
|
# If we are filtering by a version, make sure we explicitly list it.
|
|
if search_opts['version']:
|
|
try:
|
|
version_filters += (version_int(search_opts['version']),)
|
|
except UnicodeEncodeError:
|
|
pass # We didn't want to list you anyway.
|
|
|
|
versions = _get_versions(request, client.meta['versions'],
|
|
search_opts['version'])
|
|
categories = _get_categories(request, client.meta['categories'],
|
|
addon_type, category)
|
|
tags = _get_tags(request, client.meta['tags'], tag)
|
|
platforms = _get_platforms(request, client.meta['platforms'],
|
|
search_opts['platform'])
|
|
sort_tabs = _get_sorts(request, sort)
|
|
sort_opts = _get_sort_menu(request, sort)
|
|
|
|
pager = amo.utils.paginate(request, results, search_opts['limit'])
|
|
|
|
context = dict(pager=pager, query=query, tag=tag, platforms=platforms,
|
|
versions=versions, categories=categories, tags=tags,
|
|
sort_tabs=sort_tabs, sort_opts=sort_opts, sort=sort)
|
|
return jingo.render(request, template, context)
|