Support selecting more than one layer in other distro comparison search form

Provide a lazy-loaded popup for selecting layers to include in the query
instead of having it as a simple drop-down, so you can select more than
one layer.

Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
This commit is contained in:
Paul Eggleton 2018-02-14 10:13:13 +13:00
parent 8ef315c6bf
commit 26d2bd2f36
4 changed files with 154 additions and 6 deletions

View File

@ -8,7 +8,7 @@ from django.conf.urls import *
from django.views.generic import TemplateView, DetailView, ListView, RedirectView from django.views.generic import TemplateView, DetailView, ListView, RedirectView
from django.views.defaults import page_not_found from django.views.defaults import page_not_found
from django.core.urlresolvers import reverse_lazy from django.core.urlresolvers import reverse_lazy
from layerindex.views import LayerListView, LayerReviewListView, LayerReviewDetailView, RecipeSearchView, MachineSearchView, LayerDetailView, edit_layer_view, delete_layer_view, edit_layernote_view, delete_layernote_view, HistoryListView, EditProfileFormView, AdvancedRecipeSearchView, BulkChangeView, BulkChangeSearchView, bulk_change_edit_view, bulk_change_patch_view, BulkChangeDeleteView, RecipeDetailView, RedirectParamsView, ClassicRecipeSearchView, ClassicRecipeDetailView, ClassicRecipeStatsView, LayerUpdateDetailView, UpdateListView, UpdateDetailView, StatsView, publish_view from layerindex.views import LayerListView, LayerReviewListView, LayerReviewDetailView, RecipeSearchView, MachineSearchView, LayerDetailView, edit_layer_view, delete_layer_view, edit_layernote_view, delete_layernote_view, HistoryListView, EditProfileFormView, AdvancedRecipeSearchView, BulkChangeView, BulkChangeSearchView, bulk_change_edit_view, bulk_change_patch_view, BulkChangeDeleteView, RecipeDetailView, RedirectParamsView, ClassicRecipeSearchView, ClassicRecipeDetailView, ClassicRecipeStatsView, LayerUpdateDetailView, UpdateListView, UpdateDetailView, StatsView, publish_view, LayerCheckListView
from layerindex.models import LayerItem, Recipe, RecipeChangeset from layerindex.models import LayerItem, Recipe, RecipeChangeset
from rest_framework import routers from rest_framework import routers
from . import restviews from . import restviews
@ -150,5 +150,9 @@ urlpatterns = [
ClassicRecipeDetailView.as_view( ClassicRecipeDetailView.as_view(
template_name='layerindex/classicrecipedetail.html'), template_name='layerindex/classicrecipedetail.html'),
name='comparison_recipe'), name='comparison_recipe'),
url(r'^ajax/layerchecklist/(?P<branch>[-\w]+)/$',
LayerCheckListView.as_view(
template_name='layerindex/layerchecklist.html'),
name='layer_checklist'),
url(r'.*', page_not_found, kwargs={'exception': Exception("Page not Found")}) url(r'.*', page_not_found, kwargs={'exception': Exception("Page not Found")})
] ]

View File

@ -940,6 +940,13 @@ class ClassicRecipeReverseLinkWrapper(LinkWrapper):
setattr(obj, 'cover_vercmp', vercmp) setattr(obj, 'cover_vercmp', vercmp)
class LayerCheckListView(ListView):
context_object_name = 'layerbranches'
def get_queryset(self):
_check_url_branch(self.kwargs)
return LayerBranch.objects.filter(branch__name=self.kwargs['branch']).filter(layer__status__in=['P', 'X']).order_by('layer__name')
class ClassicRecipeSearchView(RecipeSearchView): class ClassicRecipeSearchView(RecipeSearchView):
def render_to_response(self, context, **kwargs): def render_to_response(self, context, **kwargs):
@ -952,7 +959,11 @@ class ClassicRecipeSearchView(RecipeSearchView):
cover_status = self.request.GET.get('cover_status', None) cover_status = self.request.GET.get('cover_status', None)
cover_verified = self.request.GET.get('cover_verified', None) cover_verified = self.request.GET.get('cover_verified', None)
category = self.request.GET.get('category', None) category = self.request.GET.get('category', None)
oe_layer = self.request.GET.get('oe_layer', None) selectedlayers_param = self.request.GET.get('selectedlayers', '')
if selectedlayers_param:
layer_ids = [int(i) for i in selectedlayers_param.split(',')]
else:
layer_ids = []
has_patches = self.request.GET.get('has_patches', '') has_patches = self.request.GET.get('has_patches', '')
needs_attention = self.request.GET.get('needs_attention', '') needs_attention = self.request.GET.get('needs_attention', '')
qreversed = self.request.GET.get('reversed', '') qreversed = self.request.GET.get('reversed', '')
@ -978,8 +989,8 @@ class ClassicRecipeSearchView(RecipeSearchView):
else: else:
init_qs = init_qs.filter(classic_category__icontains=category) init_qs = init_qs.filter(classic_category__icontains=category)
filtered = True filtered = True
if oe_layer: if layer_ids:
init_qs = init_qs.filter(cover_layerbranch__layer=oe_layer) init_qs = init_qs.filter(cover_layerbranch__layer__in=layer_ids)
if has_patches.strip(): if has_patches.strip():
if has_patches == '1': if has_patches == '1':
init_qs = init_qs.filter(patch__isnull=False).distinct() init_qs = init_qs.filter(patch__isnull=False).distinct()
@ -1015,8 +1026,8 @@ class ClassicRecipeSearchView(RecipeSearchView):
return Recipe.objects.none() return Recipe.objects.none()
if qreversed: if qreversed:
init_rqs = Recipe.objects.filter(layerbranch__branch__name='master') init_rqs = Recipe.objects.filter(layerbranch__branch__name='master')
if oe_layer: if layer_ids:
init_rqs = init_rqs.filter(layerbranch__layer=oe_layer) init_rqs = init_rqs.filter(layerbranch__layer__id__in=layer_ids)
all_values = [] all_values = []
if filtered: if filtered:
if isinstance(qs, list): if isinstance(qs, list):
@ -1057,6 +1068,16 @@ class ClassicRecipeSearchView(RecipeSearchView):
context['reversed'] = self.request.GET.get('reversed', False) context['reversed'] = self.request.GET.get('reversed', False)
context['search_form'] = search_form context['search_form'] = search_form
context['searched'] = searched context['searched'] = searched
selectedlayers_param = self.request.GET.get('selectedlayers', '')
if selectedlayers_param:
all_layer_names = dict(LayerItem.objects.all().values_list('id', 'name'))
layer_ids = [int(i) for i in selectedlayers_param.split(',')]
layer_names = [all_layer_names[i] for i in layer_ids]
context['selectedlayers_display'] = ','.join(layer_names)
else:
layer_ids = []
context['selectedlayers_display'] = ' (any)'
context['selectedlayers'] = layer_ids
return context return context

View File

@ -45,12 +45,45 @@
<tbody> <tbody>
{% for field in search_form.visible_fields %} {% for field in search_form.visible_fields %}
<tr> <tr>
{% if field.name == 'oe_layer' %}
<td>
{{ field.label }}
</td>
<td>
<div class="input-append">
<input type="text" class="input-large" id="id_selectedlayers_display" value="{{ selectedlayers_display }}" />
<a href="#layerDialog" id="id_select_layers" role="button" class="btn" data-toggle="modal">...</a>
</div>
<input type="hidden" id="id_selectedlayers" name="selectedlayers" value="{{ selectedlayers|join:"," }}" />
<div id="layerDialog" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="layerDialogLabel" aria-hidden="true">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h3 id="layerDialogLabel">Select layers to include</h3>
</div>
<div class="modal-body">
<div class="scrolling" id="id_layerdialog_list">
</div>
<div class="buttonblock">
<button type="button" class="btn" id="id_layerdialog_select_all">Select all</button>
<button type="button" class="btn buttonblock-btn" id="id_layerdialog_select_none">Select none</button>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-primary" id="id_layerdialog_ok" data-dismiss="modal" aria-hidden="true">OK</button>
<button class="btn" id="id_layerdialog_cancel" data-dismiss="modal" aria-hidden="true">Cancel</button>
</div>
</div>
</td>
{% else %}
<td> <td>
{{ field.label }} {{ field.label }}
</td> </td>
<td> <td>
{{ field }} {{ field }}
</td> </td>
{% endif %}
</tr> </tr>
{% endfor %} {% endfor %}
<tr><td><label class="checkbox"><input type="checkbox" name="compare" {% if compare %}checked{% endif %}></input>Show comparison</label></td></tr> <tr><td><label class="checkbox"><input type="checkbox" name="compare" {% if compare %}checked{% endif %}></input>Show comparison</label></td></tr>
@ -169,11 +202,80 @@
{% block scripts %} {% block scripts %}
<script> <script>
update_selected_layer_display = function() {
layernames = [];
layerids = [];
$('.filterlayercheckbox:checked').each(function() {
layernames.push($("label[for="+$(this).attr('id')+"]").html());
layerids.push($(this).attr('value'))
});
$('#id_selectedlayers').val(layerids)
if(layernames.length)
$('#id_selectedlayers_display').val(layernames)
else
$('#id_selectedlayers_display').val(' (any)')
}
update_filters_enabled = function() {
if( $('#id_reversed').is(":checked") ) {
$('#id_reversed_fields').show()
}
else {
$('#id_reversed_fields').hide()
}
}
select_layer_checkboxes = function() {
$('.filterlayercheckbox').attr('checked', false);
selectedlayers = $('#id_selectedlayers').val().split(',');
for(i in selectedlayers) {
$('#id_layercheckbox_' + selectedlayers[i]).attr('checked', true);
}
}
setup_layer_list = function() {
if( $.trim($('#id_layerdialog_list').html()) ) {
select_layer_checkboxes()
}
else {
$('#id_layerdialog_list').html('Loading...');
$('#id_layerdialog_ok').prop('disabled', true)
$('#id_layerdialog_ok').addClass('disabled')
$.ajax({
url: '{% url 'layer_checklist' 'master' %}',
dataType: 'html',
success: function( resp ) {
$('#id_layerdialog_list').html(resp);
select_layer_checkboxes()
$('#id_layerdialog_ok').prop('disabled', false)
$('#id_layerdialog_ok').removeClass('disabled')
},
error: function( req, status, err ) {
$('#id_layerdialog_list').html(err);
console.log( 'something went wrong', status, err );
}
});
}
}
$(document).ready(function() { $(document).ready(function() {
$('#id_selectedlayers_display').prop('readonly', true)
update_filters_enabled()
$('[data-toggle="tooltip"]').tooltip(); $('[data-toggle="tooltip"]').tooltip();
firstfield = $("#search-form input:text").first() firstfield = $("#search-form input:text").first()
if( ! firstfield.val() ) if( ! firstfield.val() )
firstfield.focus() firstfield.focus()
}); });
$('#id_layerdialog_select_all').click(function (e) {
$('.filterlayercheckbox').attr('checked', true);
});
$('#id_layerdialog_select_none').click(function (e) {
$('.filterlayercheckbox').attr('checked', false);
});
$('#id_layerdialog_ok').click(function (e) {
update_selected_layer_display()
});
$('#id_select_layers').click(function (e) {
setup_layer_list()
});
$('#id_reversed').click(function (e) {
update_filters_enabled()
});
</script> </script>
{% endblock %} {% endblock %}

View File

@ -0,0 +1,21 @@
{% comment %}
layerindex-web - layer check list AJAX template
Copyright (C) 2018 Intel Corporation
Licensed under the MIT license, see COPYING.MIT for details
{% endcomment %}
{% autoescape on %}
<table><tbody>
{% for layerbranch in layerbranches %}
<tr>
<td class="checkboxtd">
<input type="checkbox" class="filterlayercheckbox" value="{{ layerbranch.layer.id }}" id="id_layercheckbox_{{layerbranch.layer.id}}" />
</td>
<td><label for="id_layercheckbox_{{layerbranch.layer.id}}">{{ layerbranch.layer.name }}</label></td>
</tr>
{% endfor %}
</tbody></table>
{% endautoescape %}