Merge branch 'bug-679219-search_times_depended_on'

This commit is contained in:
Sean McArthur 2011-08-30 10:22:31 -07:00
Родитель 32ac90efa4 2c57c3116d
Коммит 107b8f700f
7 изменённых файлов: 142 добавлений и 64 удалений

24
apps/base/forms.py Normal file
Просмотреть файл

@ -0,0 +1,24 @@
from django import forms
from django.forms.util import ErrorDict
class CleanForm(forms.Form):
def full_clean(self):
"""
Cleans self.data and populates self._errors and self.cleaned_data.
Does not remove cleaned_data if there are errors.
"""
self._errors = ErrorDict()
if not self.is_bound: # Stop further processing.
return
self.cleaned_data = {}
# If the form is permitted to be empty, and none of the form data
# has changed from the initial data, short circuit any validation.
if self.empty_permitted and not self.has_changed():
return
self._clean_fields()
self._clean_form()
self._post_clean()

Просмотреть файл

@ -1033,7 +1033,9 @@ class PackageRevision(BaseModel):
if save:
# save as new version
self.save()
return self.dependencies.add(dep)
ret = self.dependencies.add(dep)
dep.package.refresh_index()
return ret
def compare_dependency_conflicts(self, dep, as_upgrade=False):
"""
@ -1117,7 +1119,9 @@ class PackageRevision(BaseModel):
'dependency (%s) removed' % dep.name)
# save as new version
self.save()
return self.dependencies.remove(dep)
ret = self.dependencies.remove(dep)
dep.package.refresh_index()
return ret
raise DependencyException(
'There is no such library in this %s' \
% self.package.get_type_name())
@ -1740,6 +1744,11 @@ class Package(BaseModel, SearchMixin):
except PackageRevision.DoesNotExist:
pass
if self.is_library():
data['times_depended'] = (Package.objects
.filter(latest__dependencies__in=self.revisions.all())
.count())
try:
es.index(data, settings.ES_INDEX, self._meta.db_table, id=self.id,
bulk=bulk)

Просмотреть файл

@ -1,34 +1,18 @@
from django import forms
from django.forms.util import ErrorDict
from django.contrib.auth.models import User
from base.forms import CleanForm
TYPE_CHOICES = (
('l', 'Libraries'),
('a', 'Add-ons'),
)
class SearchForm(forms.Form):
class SearchForm(CleanForm):
q = forms.CharField(required=False)
page = forms.IntegerField(required=False, initial=1)
type = forms.ChoiceField(required=False, choices=TYPE_CHOICES)
author = forms.ModelChoiceField(required=False, queryset=User.objects.all())
copies = forms.IntegerField(required=False, initial=0)
used = forms.IntegerField(required=False, initial=0)
def full_clean(self):
"""
Cleans self.data and populates self._errors and self.cleaned_data.
Does not remove cleaned_data if there are errors.
"""
self._errors = ErrorDict()
if not self.is_bound: # Stop further processing.
return
self.cleaned_data = {}
# If the form is permitted to be empty, and none of the form data
# has changed from the initial data, short circuit any validation.
if self.empty_permitted and not self.has_changed():
return
self._clean_fields()
self._clean_form()
self._post_clean()

Просмотреть файл

@ -51,5 +51,17 @@
</div>
</li>
{% endif %}
{% if max_times_depended %}
<li id="UsedFilter">
Used by
<span class="slider-value">{{ query.used }}</span>
or more packages:
<div class="slider">
<div class="knob"></div>
<span class="range start">0</span>
<span class="range end">{{ max_times_depended }}</span>
</div>
</li>
{% endif %}
</ul>
</section>

Просмотреть файл

@ -112,6 +112,22 @@ class TestSearch(ESTestCase):
class PackageSearchTest(ESTestCase):
fixtures = ('mozilla_user', 'users', 'core_sdk',)
def test_times_depended_on(self):
foo = create_library('foooooo')
bar = create_addon('barrrr')
bar.latest.dependency_add(foo.latest)
self.es.refresh()
qs = Package.search().filter(times_depended__gte=1)
eq_(len(qs), 1)
eq_(qs[0], foo)
class PackageHelperSearchTest(ESTestCase):
"""
search.helpers.package_search has some built-in sane defaults when
searching for Packages.

Просмотреть файл

@ -28,16 +28,27 @@ def search(request):
author = query.get('author')
if author:
filters['author'] = author.id
if query.get('copies'):
filters['copies_count__gte'] = query['copies']
else:
query['copies'] = 0
if query.get('used'):
filters['times_depended__gte'] = query['used']
else:
query['used'] = 0
results = {}
facets = {}
copies_facet = {'terms': {'field': 'copies_count'}}
times_depended_facet = {'terms': {'field': 'times_depended'}}
facets_ = {'copies': copies_facet, 'times_depended': times_depended_facet}
if type_:
filters['type'] = type_
qs = package_search(q, **filters).facet(copies={'terms':
{'field':'copies_count'}})
qs = package_search(q, **filters).facet(**facets_)
try:
results['pager'] = Paginator(qs, per_page=limit).page(page)
except EmptyPage:
@ -48,8 +59,9 @@ def search(request):
else:
# combined view
results['addons'] = package_search(q, type='a', **filters).facet(
copies={'terms':{'field':'copies_count'}})[:5]
results['libraries'] = package_search(q, type='l', **filters)[:5]
**facets_)[:5]
results['libraries'] = package_search(q, type='l', **filters).facet(
**facets_)[:5]
facets = _facets(results['addons'].facets)
facets['everyone_total'] = facets['combined_total']
template = 'aggregate.html'
@ -101,10 +113,19 @@ def _facets(facets):
max_ = copies_steps.pop()
max_copies = max(max_copies, max_)
max_times_depended = 0
if 'times_depended' in facets:
depended_steps = [t['term'] for t in facets['times_depended']]
if depended_steps:
depended_steps.sort()
max_ = depended_steps.pop()
max_times_depended = max(max_times_depended, max_)
return {
'addon_total': type_totals.get('a', 0),
'library_total': type_totals.get('l', 0),
'my_total': my_total,
'combined_total': type_totals.get('a', 0) + type_totals.get('l', 0),
'max_copies': max_copies,
'max_times_depended': max_times_depended
}

Просмотреть файл

@ -44,13 +44,16 @@ var SearchResult = new Class({
newSidebar.replaces(sidebar);
}
if (!this.slider) {
this.slider = SearchResult.setupUI();
if (!this.ui) {
SearchResult.setupUI(this);
} else {
this.slider.sanityCheck = false;
var loc = new URI(this.url);
this.slider.set(loc.getData('copies') || 0);
this.slider.sanityCheck = true;
Object.each(this.ui.sliders, function(slider, name) {
slider.sanityCheck = false;
slider.set(loc.getData(name) || 0);
slider.sanityCheck = true;
});
}
return this;
@ -80,42 +83,51 @@ SearchResult.page = function(url) {
SearchResult.fetch(String(window.location));
};
SearchResult.setupUI = function() {
var copies = $('CopiesFilter');
if (copies) {
var cSlider = copies.getElement('.slider'),
cKnob = cSlider.getElement('.knob'),
cValue = copies.getElement('.slider-value'),
cRangeEnd = cSlider.getElement('.range.end'),
end = cRangeEnd.get('text').toInt();
SearchResult.setupUI = function(result) {
var ui = { sliders: {} };
if (result) result.ui = ui;
var initialStep = Math.max(0, cValue.get('text').toInt() || 0);
var copiesSlider = new Slider(cSlider, cKnob, {
//snap: true,
range: [0, end],
initialStep: initialStep,
onChange: function(step) {
cValue.set('text', step);
},
onComplete: function(step) {
if (!this.sanityCheck) return;
var filters = ['Copies', 'Used'];
filters.forEach(function(filter) {
var container = $(filter + 'Filter'),
dataKey = filter.toLowerCase();
var loc = new URI(String(window.location));
loc.setData('copies', step);
SearchResult.page(loc);
}
});
// onComplete gets triggered many times when no dragging
// actually occurred, because we set a range and initialStep. To
// prevent those fake onComplete's from trigger anything, we
// check our sanity by stopping all onComplete's that happen
// during initialization, since sanityCheck get's set to true
// _after_ construction.
copiesSlider.sanityCheck = true;
if (container) {
var sliderEl = container.getElement('.slider'),
knobEl = sliderEl.getElement('.knob'),
valueEl = container.getElement('.slider-value'),
rangeEndEl = sliderEl.getElement('.range.end'),
end = rangeEndEl.get('text').toInt();
return copiesSlider;
}
var initialStep = Math.max(0, valueEl.get('text').toInt() || 0);
var slider = new Slider(sliderEl, knobEl, {
//snap: true,
range: [0, end],
initialStep: initialStep,
onChange: function(step) {
valueEl.set('text', step);
},
onComplete: function(step) {
if (!this.sanityCheck) return;
var loc = new URI(String(window.location));
loc.setData(dataKey, step);
SearchResult.page(loc);
}
});
// onComplete gets triggered many times when no dragging
// actually occurred, because we set a range and initialStep. To
// prevent those fake onComplete's from trigger anything, we
// check our sanity by stopping all onComplete's that happen
// during initialization, since sanityCheck get's set to true
// _after_ construction.
slider.sanityCheck = true;
ui.sliders[dataKey] = slider;
}
});
};