Permalink
Browse files

Merge pull request #216 from tallowen/advsearch

[fix bug 681106] Advanced search options. r=me
  • Loading branch information...
James Socol
James Socol committed Apr 18, 2012
2 parents a829089 + e16f06a commit 005e22e751a63bab04cc2f7d29b11f6ee45d980c
View
@@ -22,7 +22,10 @@
class SearchForm(happyforms.Form):
q = forms.CharField(widget=forms.HiddenInput, required=True)
limit = forms.CharField(widget=forms.HiddenInput, required=False)
- nonvouched_only = forms.BooleanField(required=False)
+ nonvouched_only = forms.BooleanField(label=_lazy(u'Non Vouched Only'),
+ required=False)
+ picture_only = forms.BooleanField(label=_lazy(u'Only users with photos'),
+ required=False)
def clean_limit(self):
"""Validate that this limit is numeric and greater than 1"""
@@ -25,8 +25,22 @@ <h1>{{ _('Search') }}</h1>
value="{% if form.cleaned_data %}{{ form.cleaned_data.q }}{% endif %}">
<input type="hidden" name="limit" id="limit" value="{{ limit }}">
<button type="submit" class="btn primary">
+ <span class="icon-search"></span>
{{ _('Search') }}
</button>
+ <button type="button" id="advanced" class="btn primary">
+ <span class="icon-chevron-down"></span>
+ {{ _('Advanced Options') }}
+ </button>
+ <div class="search-options">
+ {% for f in ['nonvouched_only', 'picture_only'] %}
+ <div id="{{ form[f].name }}-container"
+ class="field {{ form[f].field.widget.attrs['class'] }}">
+ {{ form[f] }}
+ {{ form[f].label_tag() }}
+ </div>
+ {% endfor %}
+ </div>
</form>
<div class="well">
{% if not form.cleaned_data %}
@@ -0,0 +1,136 @@
+import os
+
+from django.conf import settings
+
+from nose.tools import eq_
+
+from common.tests import ESTestCase
+from elasticutils import get_es
+from funfactory.urlresolvers import reverse
+
+
+class TestSearch(ESTestCase):
+ def test_search_with_space(self):
+ """Extra spaces should not impact search queries."""
+ amanda = 'Amanda Younger'
+ amandeep = 'Amandeep McIlrath'
+ url = reverse('search')
+ r = self.mozillian_client.get(url, dict(q='Am'))
+ rs = self.mozillian_client.get(url, dict(q=' Am'))
+
+ eq_(r.status_code, 200)
+ peeps = r.context['people']
+ peeps_ws = rs.context['people']
+ saw_amanda = saw_amandeep = False
+
+ for person in peeps:
+ if person.display_name == amanda:
+ saw_amanda = True
+ elif person.display_name == amandeep:
+ saw_amandeep = True
+ if saw_amanda and saw_amandeep:
+ break
+
+ assert peeps[0].id in (peeps_ws[0].id, peeps_ws[1].id)
+ self.assertTrue(saw_amanda, 'We see first person')
+ self.assertTrue(saw_amandeep, 'We see another person')
+
+ def test_nonvouched_search(self):
+ """Make sure that only non vouched users are returned on search."""
+ amanda = 'Amanda Younger'
+ amandeep = 'Amandeep McIlrath'
+ url = reverse('search')
+ r = self.mozillian_client.get(url, dict(q='Am'))
+ rnv = self.mozillian_client.get(url, dict(q='Am', nonvouched_only=1))
+
+ eq_(r.status_code, 200)
+ peeps = r.context['people']
+ peeps_nv = rnv.context['people']
+
+ saw_amanda = saw_amandeep = False
+
+ for person in peeps:
+ if person.display_name == amandeep:
+ assert person.is_vouched, 'Amanda is a Mozillian'
+ saw_amandeep = True
+ elif person.display_name == amanda:
+ if person.is_vouched:
+ self.fail('Amandeep should have pending status')
+ saw_amanda = True
+ if saw_amanda and saw_amandeep:
+ break
+
+ self.assertEqual(peeps_nv[0].display_name, amanda)
+ self.assertTrue(saw_amanda, 'We see vouched users')
+ self.assertTrue(saw_amandeep, 'We see non-vouched users')
+ assert all(not person.is_vouched for person in peeps_nv)
+
+ def test_profilepic_search(self):
+ """Make sure searching for only users with profile pics works."""
+ with open(os.path.join(os.path.dirname(__file__), 'profile-photo.jpg')) as f:
+ r = self.mozillian_client.post(reverse('profile.edit'),
+ dict(first_name='Aman', last_name='Withapic', photo=f))
+
+ if not settings.ES_DISABLED:
+ get_es().refresh(settings.ES_INDEXES['default'], timesleep=0)
+
+ amanhasapic = 'Aman Withapic'
+ amanda = 'Amanda Younger'
+ url = reverse('search')
+ r = self.mozillian_client.get(url, dict(q='Am'))
+ rpp = self.mozillian_client.get(url, dict(q='Am', picture_only=1))
+
+ eq_(r.status_code, 200)
+ peeps = r.context['people']
+ peeps_pp = rpp.context['people']
+ saw_amanda = False
+
+ # Make sure that every body has a profile picture
+ for person in peeps:
+ if person.display_name == amanda:
+ if bool(person.photo):
+ self.fail('Amanda doesnt have a profile pic')
+ saw_amanda = True
+
+ # Make sure amanda shows up in peeps
+ assert amanda in [p.display_name for p in peeps]
+ # Make sure she doesn't show up in peeps_pp
+ assert amanda not in [p.display_name for p in peeps_pp]
+ self.assertEqual(peeps_pp[0].display_name, amanhasapic)
+ self.assertTrue(saw_amanda, 'We dont see profile picture')
+
+ def test_mozillian_search_pagination(self):
+ """Tests the pagination on search.
+
+ 1. assumes no page is passed, but valid limit is passed
+ 2. assumes invalid page is passed, no limit is passed
+ 3. assumes valid page is passed, no limit is passed
+ 4. assumes valid page is passed, valid limit is passed
+ """
+ url = reverse('search')
+ r = self.mozillian_client.get(url, dict(q='Amand', limit='1'))
+ peeps = r.context['people']
+ self.assertEqual(len(peeps), 1)
+
+ r = self.mozillian_client.get(url, dict(q='Amand', page='test'))
+ peeps = r.context['people']
+ self.assertEqual(len(peeps), 2)
+
+ r = self.mozillian_client.get(url, dict(q='Amand', page='1'))
+ peeps = r.context['people']
+ self.assertEqual(len(peeps), 2)
+
+ r = self.mozillian_client.get(url, dict(q='Amand', page='test',
+ limit='1'))
+ peeps = r.context['people']
+ self.assertEqual(len(peeps), 1)
+
+ r = self.mozillian_client.get(url, dict(q='Amand', page='test',
+ limit='x'))
+ peeps = r.context['people']
+ self.assertEqual(len(peeps), 2)
+
+ r = self.mozillian_client.get(url, dict(q='Amand', page='test',
+ limit='-3'))
+ peeps = r.context['people']
+ self.assertEqual(len(peeps), 2)
@@ -8,7 +8,7 @@
from nose.tools import eq_
from pyquery import PyQuery as pq
-from common.tests import TestCase, ESTestCase
+from common.tests import TestCase
from funfactory.urlresolvers import set_url_prefix, reverse
@@ -333,78 +333,6 @@ def test_localized_search_plugin(self):
assert '/fr/search' in response.content
-class TestSearch(ESTestCase):
- def test_mozillian_search(self):
- """Test our search."""
- amanda = 'Amanda Younger'
- amandeep = 'Amandeep McIlrath'
- url = reverse('search')
- r = self.mozillian_client.get(url, dict(q='Am'))
- rs = self.mozillian_client.get(url, dict(q=' Am'))
- rnv = self.mozillian_client.get(url, dict(q='Am', nonvouched_only=1))
-
- eq_(r.status_code, 200)
- peeps = r.context['people']
- peeps_ws = rs.context['people']
- peeps_nv = rnv.context['people']
-
- saw_amandeep = saw_amanda = False
-
- for person in peeps:
- if person.display_name == amandeep:
- assert person.is_vouched, 'Amandeep is a Mozillian'
- saw_amandeep = True
- elif person.display_name == amanda:
- if person.is_vouched:
- self.fail('Amanda is pending status')
- saw_amanda = True
- if saw_amandeep and saw_amanda:
- break
-
- assert peeps[0].id in (peeps_ws[0].id, peeps_ws[1].id)
- self.assertEqual(peeps_nv[0].display_name, amanda)
- self.assertTrue(saw_amandeep, 'We see Mozillians')
- self.assertTrue(saw_amanda, 'We see Pending')
-
- assert all(not person.is_vouched for person in peeps_nv)
-
- def test_mozillian_search_pagination(self):
- """Tests the pagination on search.
-
- 1. assumes no page is passed, but valid limit is passed
- 2. assumes invalid page is passed, no limit is passed
- 3. assumes valid page is passed, no limit is passed
- 4. assumes valid page is passed, valid limit is passed
- """
- url = reverse('search')
- r = self.mozillian_client.get(url, dict(q='Amand', limit='1'))
- peeps = r.context['people']
- self.assertEqual(len(peeps), 1)
-
- r = self.mozillian_client.get(url, dict(q='Amand', page='test'))
- peeps = r.context['people']
- self.assertEqual(len(peeps), 2)
-
- r = self.mozillian_client.get(url, dict(q='Amand', page='1'))
- peeps = r.context['people']
- self.assertEqual(len(peeps), 2)
-
- r = self.mozillian_client.get(url, dict(q='Amand', page='test',
- limit='1'))
- peeps = r.context['people']
- self.assertEqual(len(peeps), 1)
-
- r = self.mozillian_client.get(url, dict(q='Amand', page='test',
- limit='x'))
- peeps = r.context['people']
- self.assertEqual(len(peeps), 2)
-
- r = self.mozillian_client.get(url, dict(q='Amand', page='test',
- limit='-3'))
- peeps = r.context['people']
- self.assertEqual(len(peeps), 2)
-
-
def _logged_in_html(response):
doc = pq(response.content)
return doc('a#logout')
View
@@ -136,6 +136,7 @@ def search(request):
num_pages = 0
limit = None
nonvouched_only = False
+ picture_only = False
people = []
show_pagination = False
form = forms.SearchForm(request.GET)
@@ -144,9 +145,10 @@ def search(request):
query = form.cleaned_data.get('q', '')
limit = form.cleaned_data['limit']
vouched = False if form.cleaned_data['nonvouched_only'] else None
+ profilepic = True if form.cleaned_data['picture_only'] else None
page = request.GET.get('page', 1)
- profiles = UserProfile.search(query, vouched=vouched)
+ profiles = UserProfile.search(query, vouched=vouched, photo=profilepic)
paginator = Paginator(profiles, limit)
@@ -165,6 +167,7 @@ def search(request):
form=form,
limit=limit,
nonvouched_only=nonvouched_only,
+ picture_only=picture_only,
show_pagination=show_pagination,
num_pages=num_pages)
View
@@ -155,10 +155,11 @@ def fields(self):
# Index group ids... for fun.
groups = list(self.groups.values_list('name', flat=True))
d.update(dict(groups=groups))
+ d.update(dict(has_photo=bool(self.photo)))
return d
@classmethod
- def search(cls, query, vouched=None):
+ def search(cls, query, vouched=None, photo=None):
"""Sensible default search for UserProfiles."""
query = query.lower().strip()
fields = ('first_name__text', 'last_name__text', 'display_name__text',
@@ -169,6 +170,8 @@ def search(cls, query, vouched=None):
s = S(cls).query(or_=q)
if vouched is not None:
s = s.filter(is_vouched=vouched)
+ if photo is not None:
+ s = s.filter(has_photo=photo)
return s
View
@@ -443,6 +443,20 @@ legend {
margin-bottom:15px;
}
+.search-options {
+ display: none;
+}
+
+.search-options input, .search-options span {
+ display: block;
+ float: left;
+}
+
+.search-options input {
+ margin-left: 40px;
+ margin-right: 10px;
+}
+
.edit-profile {
max-width: 700px;
}
View
@@ -0,0 +1,5 @@
+$(function(){
+ $('#advanced').click(function(){
+ $('.search-options').slideToggle('fast');
+ });
+});
View
@@ -97,6 +97,7 @@
'search': (
'js/libs/jquery.endless-scroll.js',
'js/infinite.js',
+ 'js/expand.js',
),
'backbone': (
'js/libs/underscore.js',

0 comments on commit 005e22e

Please sign in to comment.