filter apps by region on Home, Category Landing, Category Browse, Site Search, Search Suggestions (bug there-is-none, but bug 764033 is relevant)
This commit is contained in:
Родитель
e12009f07f
Коммит
d8844359f9
|
@ -1,7 +1,7 @@
|
|||
import logging
|
||||
from operator import attrgetter
|
||||
|
||||
import elasticutils
|
||||
import elasticutils.contrib.django as elasticutils
|
||||
import pyes.exceptions as pyes
|
||||
|
||||
import amo
|
||||
|
|
|
@ -6,7 +6,7 @@ from django.core.files.storage import default_storage as storage
|
|||
from django.db import connection, transaction
|
||||
|
||||
from celeryutils import task
|
||||
import elasticutils
|
||||
import elasticutils.contrib.django as elasticutils
|
||||
from PIL import Image
|
||||
|
||||
import amo
|
||||
|
|
|
@ -6,7 +6,7 @@ from django.db import models, transaction
|
|||
from django.utils import translation
|
||||
|
||||
import caching.base
|
||||
import elasticutils
|
||||
import elasticutils.contrib.django as elasticutils
|
||||
import multidb.pinning
|
||||
import pyes.exceptions
|
||||
import queryset_transform
|
||||
|
|
|
@ -8,7 +8,7 @@ from PIL import Image
|
|||
from django.conf import settings
|
||||
|
||||
import commonware.log
|
||||
import elasticutils
|
||||
import elasticutils.contrib.django as elasticutils
|
||||
|
||||
from amo.utils import memoize
|
||||
from applications.management.commands import dump_apps
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import logging
|
||||
from operator import itemgetter
|
||||
|
||||
import elasticutils
|
||||
import elasticutils.contrib.django as elasticutils
|
||||
from django_statsd.clients import statsd
|
||||
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ from django.forms.fields import Field
|
|||
from django.test.client import Client
|
||||
from django.utils import translation
|
||||
|
||||
import elasticutils
|
||||
import elasticutils.contrib.django as elasticutils
|
||||
import mock
|
||||
import pyes.exceptions as pyes
|
||||
import test_utils
|
||||
|
@ -179,6 +179,11 @@ class TestClient(Client):
|
|||
raise AttributeError
|
||||
|
||||
|
||||
# Test decorator for mocking elasticsearch calls in ESTestCase if we don't
|
||||
# care about ES results.
|
||||
mock_es = lambda x: mock.patch('elasticutils.get_es', spec=True, new=mock.Mock)
|
||||
|
||||
|
||||
ES_patcher = mock.patch('elasticutils.get_es', spec=True)
|
||||
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
from django.core import paginator
|
||||
|
||||
import elasticutils
|
||||
import elasticutils.contrib.django as elasticutils
|
||||
import mock
|
||||
from nose.tools import eq_
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ from django.conf import settings
|
|||
from django.core.files.storage import default_storage as storage
|
||||
from django.db.models import Count
|
||||
|
||||
import elasticutils
|
||||
import elasticutils.contrib.django as elasticutils
|
||||
from celeryutils import task
|
||||
|
||||
import amo
|
||||
|
|
|
@ -5,7 +5,7 @@ from django.conf import settings
|
|||
from django.db.models import Count, Max
|
||||
|
||||
import cronjobs
|
||||
import elasticutils
|
||||
import elasticutils.contrib.django as elasticutils
|
||||
|
||||
import amo
|
||||
import amo.utils
|
||||
|
|
|
@ -7,6 +7,7 @@ from django.utils.encoding import smart_str
|
|||
from django.views.decorators.vary import vary_on_headers
|
||||
from django.utils import translation
|
||||
|
||||
from elasticutils.contrib.django import F, S
|
||||
import commonware.log
|
||||
import jingo
|
||||
from mobility.decorators import mobile_template
|
||||
|
@ -22,6 +23,9 @@ from amo.utils import MenuItem, sorted_groupby
|
|||
from bandwagon.models import Collection
|
||||
from versions.compare import dict_from_int, version_int, version_dict
|
||||
|
||||
import mkt
|
||||
from mkt.webapps.models import Webapp
|
||||
|
||||
from .forms import ESSearchForm, SecondarySearchForm, sort_by
|
||||
|
||||
|
||||
|
@ -316,7 +320,7 @@ class BaseAjaxSearch(object):
|
|||
|
||||
def queryset(self):
|
||||
"""Get items based on ID or search by name."""
|
||||
results = []
|
||||
results = Addon.objects.none()
|
||||
q = self.request.GET.get(self.key)
|
||||
if q:
|
||||
pk = None
|
||||
|
@ -329,7 +333,9 @@ class BaseAjaxSearch(object):
|
|||
qs = Addon.objects.filter(id=int(q), disabled_by_user=False)
|
||||
elif len(q) > 2:
|
||||
# Oh, how I wish I could elastically exclude terms.
|
||||
qs = (Addon.search().query(or_=name_only_query(q.lower()))
|
||||
# (You can now, but I forgot why I was complaining to
|
||||
# begin with.)
|
||||
qs = (S(Addon).query(or_=name_only_query(q.lower()))
|
||||
.filter(is_disabled=False))
|
||||
if qs:
|
||||
results = qs.filter(type__in=self.types,
|
||||
|
@ -384,6 +390,18 @@ class WebappSuggestionsAjax(SearchSuggestionsAjax):
|
|||
res = SearchSuggestionsAjax.queryset(self)
|
||||
if self.category:
|
||||
res = res.filter(category__in=[self.category])
|
||||
|
||||
region = getattr(self.request, 'REGION', mkt.regions.WORLDWIDE)
|
||||
if region:
|
||||
excluded = Webapp.get_excluded_in(region)
|
||||
if excluded:
|
||||
if isinstance(res, S):
|
||||
# ES? Do fanciness.
|
||||
return res.filter(~F(id__in=excluded))
|
||||
else:
|
||||
# Django ORM? Do an `exclude`.
|
||||
return res.exclude(id__in=excluded)
|
||||
|
||||
return res
|
||||
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import collections
|
||||
|
||||
import elasticutils
|
||||
import elasticutils.contrib.django as elasticutils
|
||||
import pyes.exceptions as pyes
|
||||
|
||||
import amo
|
||||
|
|
|
@ -4,7 +4,7 @@ from django.db import connection, transaction
|
|||
from django.db.models import Sum, Max
|
||||
|
||||
import commonware.log
|
||||
import elasticutils
|
||||
import elasticutils.contrib.django as elasticutils
|
||||
from celeryutils import task
|
||||
|
||||
import amo
|
||||
|
|
|
@ -2,7 +2,7 @@ from django.conf import settings
|
|||
from django.core.files.storage import default_storage as storage
|
||||
|
||||
import commonware.log
|
||||
import elasticutils
|
||||
import elasticutils.contrib.django as elasticutils
|
||||
from celeryutils import task
|
||||
|
||||
from amo.decorators import set_modified_on
|
||||
|
|
|
@ -15,7 +15,7 @@ from django.views import debug
|
|||
from django.views.decorators.cache import never_cache
|
||||
|
||||
import commonware.log
|
||||
import elasticutils
|
||||
import elasticutils.contrib.django as elasticutils
|
||||
import jinja2
|
||||
import jingo
|
||||
from hera.contrib.django_forms import FlushForm
|
||||
|
|
|
@ -1200,7 +1200,8 @@ RECAPTCHA_PUBLIC_KEY = ''
|
|||
RECAPTCHA_PRIVATE_KEY = ''
|
||||
RECAPTCHA_URL = ('https://www.google.com/recaptcha/api/challenge?k=%s' %
|
||||
RECAPTCHA_PUBLIC_KEY)
|
||||
RECAPTCHA_AJAX_URL = 'https://www.google.com/recaptcha/api/js/recaptcha_ajax.js'
|
||||
RECAPTCHA_AJAX_URL = (
|
||||
'https://www.google.com/recaptcha/api/js/recaptcha_ajax.js')
|
||||
|
||||
# Send Django signals asynchronously on a background thread.
|
||||
ASYNC_SIGNALS = True
|
||||
|
@ -1341,7 +1342,7 @@ BUILDER_UPGRADE_URL = 'https://addons.mozilla.org/services/builder'
|
|||
BUILDER_VERSIONS_URL = ('https://builder.addons.mozilla.org/repackage/' +
|
||||
'sdk-versions/')
|
||||
|
||||
## Elastic Search
|
||||
## elasticsearch
|
||||
ES_HOSTS = ['127.0.0.1:9200']
|
||||
ES_INDEXES = {'default': 'amo',
|
||||
'update_counts': 'amo_stats',
|
||||
|
|
|
@ -6,12 +6,13 @@ from pyquery import PyQuery as pq
|
|||
|
||||
import amo
|
||||
import amo.tests
|
||||
from amo.tests import mock_es
|
||||
from amo.urlresolvers import reverse
|
||||
from amo.utils import urlparams
|
||||
from addons.models import AddonCategory, Category
|
||||
|
||||
import mkt
|
||||
from mkt.webapps.models import Webapp
|
||||
from mkt.webapps.models import AddonExcludedRegion as AER, Webapp
|
||||
from mkt.zadmin.models import FeaturedApp, FeaturedAppRegion
|
||||
|
||||
|
||||
|
@ -32,61 +33,75 @@ class BrowseBase(amo.tests.ESTestCase):
|
|||
eq_(r.status_code, 200)
|
||||
return sorted(x.id for x in r.context[key])
|
||||
|
||||
def make_featured(self, app, category=None):
|
||||
f = FeaturedApp.objects.create(app=app, category=category)
|
||||
# Feature in the US region.
|
||||
FeaturedAppRegion.objects.create(featured_app=f,
|
||||
region=mkt.regions.US.id)
|
||||
|
||||
def setup_featured(self):
|
||||
self.skip_if_disabled(settings.REGION_STORES)
|
||||
|
||||
amo.tests.addon_factory()
|
||||
|
||||
# Category featured.
|
||||
a = amo.tests.app_factory()
|
||||
FeaturedApp.objects.create(app=a, category=self.cat)
|
||||
self.make_featured(app=a, category=self.cat)
|
||||
|
||||
b = amo.tests.app_factory()
|
||||
FeaturedApp.objects.create(app=b, category=self.cat)
|
||||
self.make_featured(app=b, category=self.cat)
|
||||
|
||||
# Home featured.
|
||||
c = amo.tests.app_factory()
|
||||
FeaturedApp.objects.create(app=c, category=None)
|
||||
|
||||
# Make these featured in the US region.
|
||||
for f in FeaturedApp.objects.all():
|
||||
FeaturedAppRegion.objects.create(featured_app=f,
|
||||
region=mkt.regions.US.id)
|
||||
self.make_featured(app=c, category=None)
|
||||
|
||||
return a, b, c
|
||||
|
||||
def setup_popular(self):
|
||||
# TODO: Figure out why ES flakes out on every other test run!
|
||||
# (I'm starting to think the "elastic" in elasticsearch is symbolic
|
||||
# of how our problems keep bouncing back. I thought elastic had more
|
||||
# potential. Maybe it's too young? I play with an elastic instrument;
|
||||
# would you like to join my rubber band? [P.S. If you can help in any
|
||||
# way, pun-wise or code-wise, please don't hesitate to do so.] In the
|
||||
# meantime, SkipTest is the rubber band to our elastic problems.)
|
||||
# When run individually these tests always pass fine.
|
||||
# But when run alongside all the other tests, they sometimes fail.
|
||||
# WTMF.
|
||||
raise SkipTest
|
||||
|
||||
amo.tests.addon_factory()
|
||||
|
||||
# Popular without a category.
|
||||
a = amo.tests.app_factory()
|
||||
self.refresh()
|
||||
self.skip_if_disabled(settings.REGION_STORES)
|
||||
|
||||
# Popular for this category.
|
||||
b = amo.tests.app_factory()
|
||||
AddonCategory.objects.create(addon=b, category=self.cat)
|
||||
b.save()
|
||||
a = amo.tests.app_factory()
|
||||
AddonCategory.objects.create(addon=a, category=self.cat)
|
||||
a.save()
|
||||
|
||||
# Popular and category featured and home featured.
|
||||
self.make_featured(webapp=self.webapp, group='category')
|
||||
self.make_featured(webapp=self.webapp, group='home')
|
||||
self.webapp.save()
|
||||
self.make_featured(app=self.webapp, category=self.cat)
|
||||
self.make_featured(app=self.webapp, category=None)
|
||||
|
||||
# Something's really up.
|
||||
self.refresh()
|
||||
|
||||
return a, b
|
||||
return a
|
||||
|
||||
def _test_popular(self, url, pks):
|
||||
def _test_featured(self):
|
||||
"""This is common to / and /apps/, so let's be DRY."""
|
||||
a, b, c = self.setup_featured()
|
||||
# Check that the Home featured app is shown only in US region.
|
||||
for region in mkt.regions.REGIONS_DICT:
|
||||
eq_(self.get_pks('featured', self.url, {'region': region}),
|
||||
[c.id] if region == 'us' else [])
|
||||
|
||||
def _test_featured_region_exclusions(self):
|
||||
a, b, c = self.setup_featured()
|
||||
AER.objects.create(addon=c, region=mkt.regions.BR.id)
|
||||
|
||||
# Feature this app in all regions.
|
||||
f = c.featuredapp_set.all()[0]
|
||||
|
||||
for region_id in mkt.regions.REGIONS_CHOICES_ID_DICT:
|
||||
# `setup_featured` already added this to the US region.
|
||||
if region_id == mkt.regions.US.id:
|
||||
continue
|
||||
FeaturedAppRegion.objects.create(featured_app=f,
|
||||
region=region_id)
|
||||
|
||||
for region in mkt.regions.REGIONS_DICT:
|
||||
eq_(self.get_pks('featured', self.url, {'region': region}),
|
||||
[] if region == 'br' else [c.id])
|
||||
|
||||
def _test_popular_pks(self, url, pks):
|
||||
r = self.client.get(self.url)
|
||||
eq_(r.status_code, 200)
|
||||
results = r.context['popular']
|
||||
|
@ -99,6 +114,19 @@ class BrowseBase(amo.tests.ESTestCase):
|
|||
reverse=True)
|
||||
eq_(list(results), expected)
|
||||
|
||||
def _test_popular(self):
|
||||
a = self.setup_popular()
|
||||
# Check that these apps are shown.
|
||||
self._test_popular_pks(self.url, [self.webapp.id, a.id])
|
||||
|
||||
def _test_popular_region_exclusions(self):
|
||||
a = self.setup_popular()
|
||||
AER.objects.create(addon=self.webapp, region=mkt.regions.BR.id)
|
||||
|
||||
for region in mkt.regions.REGIONS_DICT:
|
||||
eq_(self.get_pks('popular', self.url, {'region': region}),
|
||||
[a.id] if region == 'br' else [self.webapp.id, a.id])
|
||||
|
||||
|
||||
class TestIndexLanding(BrowseBase):
|
||||
|
||||
|
@ -106,22 +134,25 @@ class TestIndexLanding(BrowseBase):
|
|||
super(TestIndexLanding, self).setUp()
|
||||
self.url = reverse('browse.apps')
|
||||
|
||||
@mock_es
|
||||
def test_good_cat(self):
|
||||
r = self.client.get(self.url)
|
||||
eq_(r.status_code, 200)
|
||||
self.assertTemplateUsed(r, 'browse/landing.html')
|
||||
|
||||
@mock_es
|
||||
def test_featured(self):
|
||||
a, b, c = self.setup_featured()
|
||||
# Check that the non-category-featured app is shown only in US region.
|
||||
for region in mkt.regions.REGIONS_DICT:
|
||||
eq_(self.get_pks('featured', self.url, {'region': region}),
|
||||
[c.id] if region == 'us' else [])
|
||||
self._test_featured()
|
||||
|
||||
@mock_es
|
||||
def test_featured_region_exclusions(self):
|
||||
self._test_featured_region_exclusions()
|
||||
|
||||
def test_popular(self):
|
||||
a, b = self.setup_popular()
|
||||
# Check that these apps are shown on the category landing page.
|
||||
self._test_popular(self.url, [self.webapp.id, a.id, b.id])
|
||||
self._test_popular()
|
||||
|
||||
def test_popular_region_exclusions(self):
|
||||
self._test_popular_region_exclusions()
|
||||
|
||||
|
||||
class TestIndexSearch(BrowseBase):
|
||||
|
@ -163,11 +194,13 @@ class TestCategoryLanding(BrowseBase):
|
|||
return Category.objects.create(name='Slap Tickling', slug='booping',
|
||||
type=amo.ADDON_WEBAPP)
|
||||
|
||||
@mock_es
|
||||
def test_good_cat(self):
|
||||
r = self.client.get(self.url)
|
||||
eq_(r.status_code, 200)
|
||||
self.assertTemplateUsed(r, 'browse/landing.html')
|
||||
|
||||
@mock_es
|
||||
def test_bad_cat(self):
|
||||
r = self.client.get(reverse('browse.apps', args=['xxx']))
|
||||
eq_(r.status_code, 404)
|
||||
|
@ -194,15 +227,26 @@ class TestCategoryLanding(BrowseBase):
|
|||
eq_(self.get_pks('featured', new_cat_url), [])
|
||||
|
||||
def test_popular(self):
|
||||
a, b = self.setup_popular()
|
||||
a = self.setup_popular()
|
||||
|
||||
# Check that these apps are shown for this category.
|
||||
self._test_popular(self.url, [self.webapp.id, b.id])
|
||||
self._test_popular_pks(self.url, [self.webapp.id, a.id])
|
||||
|
||||
# Check that these apps are not shown for another category.
|
||||
new_cat_url = reverse('browse.apps', args=[self.get_new_cat().slug])
|
||||
eq_(self.get_pks('popular', new_cat_url), [])
|
||||
|
||||
def test_popular_region_exclusions(self):
|
||||
a = self.setup_popular()
|
||||
|
||||
AER.objects.create(addon=self.webapp, region=mkt.regions.BR.id)
|
||||
|
||||
for region in mkt.regions.REGIONS_DICT:
|
||||
print region, self.get_pks('popular', self.url, {'region': region})
|
||||
eq_(self.get_pks('popular', self.url, {'region': region}),
|
||||
[a.id] if region == 'br' else [self.webapp.id, a.id])
|
||||
|
||||
@mock_es
|
||||
def test_search_category(self):
|
||||
# Ensure category got set in the search form.
|
||||
r = self.client.get(self.url)
|
||||
|
@ -216,11 +260,13 @@ class TestCategorySearch(BrowseBase):
|
|||
self.url = reverse('browse.apps',
|
||||
args=[self.cat.slug]) + '?sort=downloads'
|
||||
|
||||
@mock_es
|
||||
def test_good_cat(self):
|
||||
r = self.client.get(self.url)
|
||||
eq_(r.status_code, 200)
|
||||
self.assertTemplateUsed(r, 'search/results.html')
|
||||
|
||||
@mock_es
|
||||
def test_bad_cat(self):
|
||||
r = self.client.get(reverse('browse.apps', args=['xxx']),
|
||||
{'sort': 'downloads'})
|
||||
|
@ -250,6 +296,7 @@ class TestCategorySearch(BrowseBase):
|
|||
urlparams(reverse('search.search'), cat=self.cat.id,
|
||||
sort='downloads'))
|
||||
|
||||
@mock_es
|
||||
def test_search_category(self):
|
||||
# Ensure category got preserved in search term.
|
||||
r = self.client.get(self.url)
|
||||
|
|
|
@ -15,11 +15,11 @@ def _landing(request, category=None):
|
|||
category = get_object_or_404(
|
||||
Category.objects.filter(type=amo.ADDON_WEBAPP, weight__gte=0),
|
||||
slug=category)
|
||||
featured = Webapp.featured(category, region)
|
||||
popular = Webapp.popular().filter(category=category.id)
|
||||
featured = Webapp.featured(cat=category, region=region)
|
||||
popular = Webapp.popular(cat=category, region=region)
|
||||
else:
|
||||
popular = Webapp.popular()
|
||||
featured = Webapp.featured(None, region)
|
||||
popular = Webapp.popular(region=region)
|
||||
featured = Webapp.featured(region=region)
|
||||
|
||||
return jingo.render(request, 'browse/landing.html', {
|
||||
'category': category,
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
from django.conf import settings
|
||||
from django.db import models
|
||||
|
||||
import amo
|
||||
from amo.models import ModelBase
|
||||
|
||||
|
||||
class MdnCache(amo.models.ModelBase):
|
||||
class MdnCache(ModelBase):
|
||||
|
||||
name = models.CharField(max_length=255)
|
||||
title = models.CharField(max_length=255, default='', blank=True)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
from nose.tools import eq_
|
||||
|
||||
from amo.tests import app_factory
|
||||
from amo.tests import app_factory, mock_es
|
||||
from amo.urlresolvers import reverse
|
||||
|
||||
import mkt
|
||||
|
@ -17,18 +17,21 @@ class TestHome(BrowseBase):
|
|||
assert self.client.login(username='steamcube@mozilla.com',
|
||||
password='password')
|
||||
|
||||
@mock_es
|
||||
def test_page(self):
|
||||
r = self.client.get(self.url)
|
||||
eq_(r.status_code, 200)
|
||||
self.assertTemplateUsed(r, 'home/home.html')
|
||||
|
||||
@mock_es
|
||||
def test_featured(self):
|
||||
a, b, c = self.setup_featured()
|
||||
# Check that the Home featured app is shown only in US region.
|
||||
for region in mkt.regions.REGIONS_DICT:
|
||||
eq_(self.get_pks('featured', self.url, {'region': region}),
|
||||
[c.id] if region == 'us' else [])
|
||||
self._test_featured()
|
||||
|
||||
@mock_es
|
||||
def test_featured_region_exclusions(self):
|
||||
self._test_featured_region_exclusions()
|
||||
|
||||
@mock_es
|
||||
def test_featured_fallback_to_worldwide(self):
|
||||
a, b, c = self.setup_featured()
|
||||
|
||||
|
@ -49,6 +52,7 @@ class TestHome(BrowseBase):
|
|||
expected)
|
||||
|
||||
def test_popular(self):
|
||||
a, b = self.setup_popular()
|
||||
# Check that these apps are shown.
|
||||
self._test_popular(self.url, [self.webapp.id, a.id, b.id])
|
||||
self._test_popular()
|
||||
|
||||
def test_popular_region_exclusions(self):
|
||||
self._test_popular_region_exclusions()
|
||||
|
|
|
@ -9,11 +9,10 @@ def home(request):
|
|||
"""The home page."""
|
||||
if not getattr(request, 'can_view_consumer', True):
|
||||
return jingo.render(request, 'home/home_walled.html')
|
||||
featured = Webapp.featured(
|
||||
cat=None,
|
||||
region=getattr(request, 'REGION', mkt.regions.WORLDWIDE))
|
||||
popular = Webapp.popular()[:10]
|
||||
latest = Webapp.latest()[:10]
|
||||
region = getattr(request, 'REGION', mkt.regions.WORLDWIDE)
|
||||
featured = Webapp.featured(region=region)
|
||||
popular = Webapp.popular(region=region)[:10]
|
||||
latest = Webapp.latest(region=region)[:10]
|
||||
return jingo.render(request, 'home/home.html', {
|
||||
'featured': featured,
|
||||
'popular': popular,
|
||||
|
|
|
@ -29,7 +29,7 @@ class SearchResource(MarketplaceResource):
|
|||
raise self.form_errors(form)
|
||||
|
||||
# Search specific processing of the results.
|
||||
qs = _get_query(request, form, form.cleaned_data)
|
||||
qs = _get_query(request)
|
||||
qs = _filter_search(qs, form.cleaned_data)
|
||||
res = amo.utils.paginate(request, qs)
|
||||
|
||||
|
|
|
@ -9,12 +9,13 @@ from addons.models import AddonCategory, AddonDeviceType, Category
|
|||
from amo.helpers import numberfmt
|
||||
from amo.urlresolvers import reverse
|
||||
from amo.utils import urlparams
|
||||
from mkt.search.forms import DEVICE_CHOICES_IDS
|
||||
from mkt.webapps.models import Webapp
|
||||
from mkt.webapps.tests.test_views import PaidAppMixin
|
||||
|
||||
from search.tests.test_views import TestAjaxSearch
|
||||
|
||||
import mkt
|
||||
from mkt.search.forms import DEVICE_CHOICES_IDS
|
||||
from mkt.webapps.tests.test_views import PaidAppMixin
|
||||
from mkt.webapps.models import AddonExcludedRegion as AER, Webapp
|
||||
|
||||
|
||||
class SearchBase(amo.tests.ESTestCase):
|
||||
|
||||
|
@ -69,6 +70,7 @@ class TestWebappSearch(PaidAppMixin, SearchBase):
|
|||
self.apps.append(app)
|
||||
self.refresh()
|
||||
|
||||
@amo.tests.mock_es
|
||||
def test_page(self):
|
||||
r = self.client.get(self.url)
|
||||
eq_(r.status_code, 200)
|
||||
|
@ -130,6 +132,7 @@ class TestWebappSearch(PaidAppMixin, SearchBase):
|
|||
def test_known_cat(self):
|
||||
self.check_cat_filter({'cat': self.cat.id})
|
||||
|
||||
@amo.tests.mock_es
|
||||
def test_unknown_cat(self):
|
||||
# `cat=999` should get removed from the querystring.
|
||||
r = self.client.get(self.url, {'price': 'free', 'cat': '999'})
|
||||
|
@ -285,38 +288,69 @@ class TestWebappSearch(PaidAppMixin, SearchBase):
|
|||
self.assert3xx(r, urlparams(url, price='free', sort='downloads'),
|
||||
302)
|
||||
|
||||
def test_region_exclusions(self):
|
||||
AER.objects.create(addon=self.webapp, region=mkt.regions.BR.id)
|
||||
for region in mkt.regions.REGIONS_DICT:
|
||||
self.check_results({'q': 'Steam', 'region': region},
|
||||
[] if region == 'br' else [self.webapp.id])
|
||||
|
||||
class SuggestionsTests(TestAjaxSearch):
|
||||
|
||||
class TestSuggestions(TestAjaxSearch):
|
||||
|
||||
def setUp(self):
|
||||
super(TestSuggestions, self).setUp()
|
||||
self.url = reverse('search.apps_ajax')
|
||||
|
||||
self.c1 = Category.objects.create(name='groovy',
|
||||
type=amo.ADDON_WEBAPP)
|
||||
self.c2 = Category.objects.create(name='awesome',
|
||||
type=amo.ADDON_WEBAPP)
|
||||
|
||||
self.w1 = Webapp.objects.create(status=amo.STATUS_PUBLIC,
|
||||
name='groovy app 1')
|
||||
self.w2 = Webapp.objects.create(status=amo.STATUS_PUBLIC,
|
||||
name='awesome app 2')
|
||||
|
||||
AddonCategory.objects.create(category=self.c1, addon=self.w1)
|
||||
AddonCategory.objects.create(category=self.c2, addon=self.w2)
|
||||
|
||||
self.reindex(Webapp)
|
||||
|
||||
def check_suggestions(self, url, params, addons=()):
|
||||
r = self.client.get(url + '?' + params)
|
||||
eq_(r.status_code, 200)
|
||||
|
||||
data = json.loads(r.content)
|
||||
data.sort(key=lambda x: x['id'])
|
||||
addons.sort(key=lambda x: x.id)
|
||||
eq_(len(data), len(addons))
|
||||
|
||||
data = sorted(data, key=lambda x: int(x['id']))
|
||||
addons = sorted(addons, key=lambda x: x.id)
|
||||
|
||||
for got, expected in zip(data, addons):
|
||||
eq_(int(got['id']), expected.id)
|
||||
eq_(got['name'], unicode(expected.name))
|
||||
|
||||
def test_webapp_search(self):
|
||||
url = reverse('search.apps_ajax')
|
||||
c1 = Category.objects.create(name='groovy',
|
||||
type=amo.ADDON_WEBAPP)
|
||||
c2 = Category.objects.create(name='awesome',
|
||||
type=amo.ADDON_WEBAPP)
|
||||
g1 = Webapp.objects.create(status=amo.STATUS_PUBLIC,
|
||||
name='groovy app 1',
|
||||
type=amo.ADDON_WEBAPP)
|
||||
a2 = Webapp.objects.create(status=amo.STATUS_PUBLIC,
|
||||
name='awesome app 2',
|
||||
type=amo.ADDON_WEBAPP)
|
||||
AddonCategory.objects.create(category=c1, addon=g1)
|
||||
AddonCategory.objects.create(category=c2, addon=a2)
|
||||
self.client.login(username='admin@mozilla.com', password='password')
|
||||
for a in Webapp.objects.all():
|
||||
a.save()
|
||||
self.refresh()
|
||||
self.check_suggestions(url, "q=app&category=", addons=[g1, a2])
|
||||
self.check_suggestions(url, "q=app&category=%d" % c1.id, addons=[g1])
|
||||
self.check_suggestions(url, "q=app&category=%d" % c2.id, addons=[a2])
|
||||
self.check_suggestions(self.url,
|
||||
'q=app&category=', addons=[self.w1, self.w2])
|
||||
self.check_suggestions(self.url,
|
||||
'q=app&category=%d' % self.c1.id, addons=[self.w1])
|
||||
self.check_suggestions(self.url,
|
||||
'q=app&category=%d' % self.c2.id, addons=[self.w2])
|
||||
|
||||
def test_region_exclusions(self):
|
||||
AER.objects.create(addon=self.w2, region=mkt.regions.BR.id)
|
||||
|
||||
self.check_suggestions(self.url,
|
||||
'region=br&q=app&category=', addons=[self.w1])
|
||||
self.check_suggestions(self.url,
|
||||
'region=br&q=app&category=%d' % self.c1.id, addons=[self.w1])
|
||||
self.check_suggestions(self.url,
|
||||
'region=br&q=app&category=%d' % self.c2.id, addons=[])
|
||||
|
||||
self.check_suggestions(self.url,
|
||||
'region=ca&q=app&category=', addons=[self.w1, self.w2])
|
||||
self.check_suggestions(self.url,
|
||||
'region=ca&q=app&category=%d' % self.c1.id, addons=[self.w1])
|
||||
self.check_suggestions(self.url,
|
||||
'region=ca&q=app&category=%d' % self.c2.id, addons=[self.w2])
|
||||
|
|
|
@ -9,7 +9,10 @@ from amo.decorators import json_view
|
|||
from amo.urlresolvers import reverse
|
||||
from apps.addons.models import Category
|
||||
from apps.search.views import name_query, WebappSuggestionsAjax
|
||||
|
||||
import mkt
|
||||
from mkt.webapps.models import Webapp
|
||||
|
||||
from . import forms
|
||||
|
||||
|
||||
|
@ -100,11 +103,9 @@ def sort_sidebar(query, form):
|
|||
for key, text in form.fields['sort'].choices]
|
||||
|
||||
|
||||
def _get_query(request, form, query):
|
||||
return (Webapp.search()
|
||||
.query(type=amo.ADDON_WEBAPP, status=amo.STATUS_PUBLIC,
|
||||
is_disabled=False)
|
||||
.facet(categories={'terms': {'field': 'category', 'size': 200}}))
|
||||
def _get_query(request):
|
||||
region = getattr(request, 'REGION', mkt.regions.WORLDWIDE)
|
||||
return Webapp.from_search(region=region).facet('category')
|
||||
|
||||
|
||||
def _app_search(request, category=None, browse=None):
|
||||
|
@ -118,11 +119,11 @@ def _app_search(request, category=None, browse=None):
|
|||
sort='downloads',
|
||||
price='free')}
|
||||
|
||||
qs = _get_query(request, form, query)
|
||||
qs = _get_query(request)
|
||||
qs = _filter_search(qs, query)
|
||||
|
||||
pager = amo.utils.paginate(request, qs)
|
||||
facets = pager.object_list.facets
|
||||
facets = pager.object_list.facet_counts()
|
||||
|
||||
if category or browse:
|
||||
if query.get('price') == 'free':
|
||||
|
@ -136,7 +137,7 @@ def _app_search(request, category=None, browse=None):
|
|||
else:
|
||||
sort_opts = form.fields['sort'].choices
|
||||
|
||||
cats = [f['term'] for f in facets['categories']]
|
||||
cats = [f['term'] for f in facets['category']]
|
||||
categories = Category.objects.filter(type=amo.ADDON_WEBAPP, id__in=cats)
|
||||
|
||||
# If category is not listed as a facet, then remove `cat` and redirect.
|
||||
|
|
|
@ -2,7 +2,7 @@ from decimal import Decimal
|
|||
|
||||
from django.db.models import Count, Q, Sum
|
||||
|
||||
import elasticutils
|
||||
import elasticutils.contrib.django as elasticutils
|
||||
import pyes.exceptions as pyes
|
||||
|
||||
import amo
|
||||
|
|
|
@ -2,7 +2,7 @@ from collections import defaultdict
|
|||
|
||||
from celeryutils import task
|
||||
import commonware.log
|
||||
import elasticutils
|
||||
import elasticutils.contrib.django as elasticutils
|
||||
|
||||
from . import search
|
||||
from mkt.inapp_pay.models import InappPayment
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import json
|
||||
import os
|
||||
import urlparse
|
||||
import uuid
|
||||
|
@ -12,6 +11,7 @@ from django.dispatch import receiver
|
|||
from django.utils.http import urlquote
|
||||
|
||||
import commonware.log
|
||||
from elasticutils.contrib.django import F, S
|
||||
import waffle
|
||||
from tower import ugettext as _
|
||||
|
||||
|
@ -218,6 +218,7 @@ class Webapp(Addon):
|
|||
return 'icons' in data
|
||||
|
||||
def get_manifest_json(self):
|
||||
import json
|
||||
try:
|
||||
# The first file created for each version of the web app
|
||||
# is the manifest.
|
||||
|
@ -409,20 +410,30 @@ class Webapp(Addon):
|
|||
.values_list('addon', flat=True))
|
||||
|
||||
@classmethod
|
||||
def from_search(cls):
|
||||
return cls.search().filter(type=amo.ADDON_WEBAPP,
|
||||
def from_search(cls, cat=None, region=None):
|
||||
filters = dict(type=amo.ADDON_WEBAPP,
|
||||
status=amo.STATUS_PUBLIC,
|
||||
is_disabled=False)
|
||||
|
||||
@classmethod
|
||||
def popular(cls):
|
||||
"""Elastically grab the most popular apps."""
|
||||
return cls.from_search().order_by('-weekly_downloads')
|
||||
if cat:
|
||||
filters.update(category=cat.id)
|
||||
|
||||
if region:
|
||||
excluded = cls.get_excluded_in(region)
|
||||
if excluded:
|
||||
return S(cls).query(**filters).filter(~F(id__in=excluded))
|
||||
|
||||
return S(cls).query(**filters)
|
||||
|
||||
@classmethod
|
||||
def latest(cls):
|
||||
def popular(cls, cat=None, region=None):
|
||||
"""Elastically grab the most popular apps."""
|
||||
return cls.from_search(cat, region).order_by('-weekly_downloads')
|
||||
|
||||
@classmethod
|
||||
def latest(cls, cat=None, region=None):
|
||||
"""Elastically grab the most recent apps."""
|
||||
return cls.from_search().order_by('-created')
|
||||
return cls.from_search(cat, region).order_by('-created')
|
||||
|
||||
@classmethod
|
||||
def category(cls, slug):
|
||||
|
|
Загрузка…
Ссылка в новой задаче