Add duplicate recipes/classes page

Add page that lists recipes and classes "duplicated" across different
layers.

Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
This commit is contained in:
Paul Eggleton 2013-05-13 10:59:12 +01:00
parent 9af1144928
commit 89f9e6afe8
5 changed files with 156 additions and 25 deletions

2
TODO
View File

@ -20,7 +20,7 @@ Later:
* Return to last page (review/detail) after editing (with success alert)?
* Cancel button on edit form?
* Query backend service? i.e. special URL to query information for external apps/scripts
* Tool for finding/comparing duplicate recipes?
* Add comparison to duplicates page
* Tool for editing SUMMARY/DESCRIPTION? [Paul working on this]
* Dynamic loading/filtering for recipes list
* Some way to notify the user when they search for something that has been renamed / replaced / deprecated?

View File

@ -8,7 +8,7 @@ from django.conf.urls.defaults import *
from django.views.generic import TemplateView, DetailView, ListView
from django.views.defaults import page_not_found
from layerindex.models import LayerItem, Recipe
from layerindex.views import LayerListView, LayerReviewListView, LayerReviewDetailView, RecipeSearchView, MachineSearchView, PlainTextListView, LayerDetailView, edit_layer_view, delete_layer_view, edit_layernote_view, delete_layernote_view, switch_branch_view, HistoryListView, EditProfileFormView
from layerindex.views import LayerListView, LayerReviewListView, LayerReviewDetailView, RecipeSearchView, MachineSearchView, PlainTextListView, LayerDetailView, edit_layer_view, delete_layer_view, edit_layernote_view, delete_layernote_view, switch_branch_view, HistoryListView, EditProfileFormView, DuplicatesView
urlpatterns = patterns('',
url(r'^$',
@ -71,6 +71,10 @@ urlpatterns = patterns('',
HistoryListView.as_view(
template_name='layerindex/history.html'),
name='history_list'),
url(r'^duplicates/$',
DuplicatesView.as_view(
template_name='layerindex/duplicates.html'),
name='duplicates'),
url(r'^profile/$',
EditProfileFormView.as_view(
template_name='layerindex/profile.html'),

View File

@ -9,14 +9,14 @@ from django.http import HttpResponse, HttpResponseRedirect, HttpResponseForbidde
from django.core.urlresolvers import reverse
from django.core.exceptions import PermissionDenied
from django.template import RequestContext
from layerindex.models import Branch, LayerItem, LayerMaintainer, LayerBranch, LayerDependency, LayerNote, Recipe, Machine
from layerindex.models import Branch, LayerItem, LayerMaintainer, LayerBranch, LayerDependency, LayerNote, Recipe, Machine, BBClass
from datetime import datetime
from django.views.generic import DetailView, ListView
from django.views.generic import TemplateView, DetailView, ListView
from django.views.generic.edit import UpdateView
from layerindex.forms import EditLayerForm, LayerMaintainerFormSet, EditNoteForm, EditProfileForm
from django.db import transaction
from django.contrib.auth.models import User, Permission
from django.db.models import Q
from django.db.models import Q, Count
from django.core.mail import EmailMessage
from django.template.loader import get_template
from django.template import Context
@ -256,25 +256,7 @@ class LayerReviewDetailView(LayerDetailView):
raise PermissionDenied
return super(LayerReviewDetailView, self).dispatch(request, *args, **kwargs)
class RecipeSearchView(ListView):
context_object_name = 'recipe_list'
paginate_by = 50
def get_queryset(self):
query_string = self.request.GET.get('q', '')
init_qs = Recipe.objects.filter(layerbranch__branch__name=self.request.session.get('branch', 'master'))
if query_string.strip():
entry_query = simplesearch.get_query(query_string, ['pn', 'summary', 'description', 'filename'])
qs = init_qs.filter(entry_query).order_by('pn', 'layerbranch__layer')
else:
if 'q' in self.request.GET:
qs = init_qs.order_by('pn', 'layerbranch__layer')
else:
# It's a bit too slow to return all records by default, and most people
# won't actually want that (if they do they can just hit the search button
# with no query string)
return Recipe.objects.none()
def recipes_preferred_count(qs):
# Add extra column so we can show "duplicate" recipes from other layers de-emphasised
# (it's a bit crude having to do this using SQL but I couldn't find a better way...)
return qs.extra(
@ -296,11 +278,51 @@ AND layer2.index_preference > layer1.index_preference
},
)
class RecipeSearchView(ListView):
context_object_name = 'recipe_list'
paginate_by = 50
def get_queryset(self):
query_string = self.request.GET.get('q', '')
init_qs = Recipe.objects.filter(layerbranch__branch__name=self.request.session.get('branch', 'master'))
if query_string.strip():
entry_query = simplesearch.get_query(query_string, ['pn', 'summary', 'description', 'filename'])
qs = init_qs.filter(entry_query).order_by('pn', 'layerbranch__layer')
else:
if 'q' in self.request.GET:
qs = init_qs.order_by('pn', 'layerbranch__layer')
else:
# It's a bit too slow to return all records by default, and most people
# won't actually want that (if they do they can just hit the search button
# with no query string)
return Recipe.objects.none()
return recipes_preferred_count(qs)
def get_context_data(self, **kwargs):
context = super(RecipeSearchView, self).get_context_data(**kwargs)
context['search_keyword'] = self.request.GET.get('q', '')
return context
class DuplicatesView(TemplateView):
def get_recipes(self):
init_qs = Recipe.objects.filter(layerbranch__branch__name=self.request.session.get('branch', 'master'))
dupes = init_qs.values('pn').annotate(Count('layerbranch', distinct=True)).filter(layerbranch__count__gt=1)
qs = init_qs.all().filter(pn__in=[item['pn'] for item in dupes]).order_by('pn', 'layerbranch__layer')
return recipes_preferred_count(qs)
def get_classes(self):
init_qs = BBClass.objects.filter(layerbranch__branch__name=self.request.session.get('branch', 'master'))
dupes = init_qs.values('name').annotate(Count('layerbranch', distinct=True)).filter(layerbranch__count__gt=1)
qs = init_qs.all().filter(name__in=[item['name'] for item in dupes]).order_by('name', 'layerbranch__layer')
return qs
def get_context_data(self, **kwargs):
context = super(DuplicatesView, self).get_context_data(**kwargs)
context['recipes'] = self.get_recipes()
context['classes'] = self.get_classes()
return context
class MachineSearchView(ListView):
context_object_name = 'machine_list'
paginate_by = 50

View File

@ -63,6 +63,15 @@
{% endif %}
</a></li>
{% endif %}
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
Tools
<b class="caret"></b>
</a>
<ul class="dropdown-menu">
<li><a href="{% url duplicates %}">Duplicates</a></li>
</ul>
</li>
<li class="divider-vertical"></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">

View File

@ -0,0 +1,96 @@
{% extends "base.html" %}
{% load i18n %}
{% comment %}
layerindex-web - duplicates page template
Copyright (C) 2013 Intel Corporation
Licensed under the MIT license, see COPYING.MIT for details
{% endcomment %}
<!--
{% block title_append %} - duplicates{% endblock %}
-->
{% block content %}
{% autoescape on %}
<div class="row-fluid">
<div class="span9 offset1">
<h2>Duplicate recipes</h2>
{% if recipes %}
<p>Recipes with the same name in different layers:</p>
<table class="table table-striped table-bordered recipestable">
<thead>
<tr>
<th>Recipe name</th>
<th>Version</th>
<th class="span9">Description</th>
<th>Layer</th>
</tr>
</thead>
<tbody>
{% for recipe in recipes %}
<tr {% if recipe.preferred_count > 0 %}class="muted"{% endif %}>
<td><a href="{% url recipe recipe.id %}">{{ recipe.name }}</a></td>
<td>{{ recipe.pv }}</td>
<td>{{ recipe.short_desc }}</td>
<td><a href="{% url layer_item recipe.layerbranch.layer.name %}">{{ recipe.layerbranch.layer.name }}</a></td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<p>No duplicate recipes in database.</p>
{% endif %}
</div>
</div>
<div class="row-fluid">
<div class="span9 offset1">
<h2>Duplicate classes</h2>
{% if classes %}
<p>Classes with the same name in different layers:</p>
<table class="table table-striped table-bordered recipestable">
<thead>
<tr>
<th>Class name</th>
<th>Layer</th>
</tr>
</thead>
<tbody>
{% for class in classes %}
<tr>
<td><a href="{% url class.vcs_web_url %}">{{ class.name }}</a></td>
<td><a href="{% url layer_item class.layerbranch.layer.name %}">{{ class.layerbranch.layer.name }}</a></td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<p>No duplicate classes in database.</p>
{% endif %}
</div>
</div>
{% endautoescape %}
{% endblock %}
{% block scripts %}
<script>
$(document).ready(function() {
firstfield = $("#filter-form input:text").first()
if( ! firstfield.val() )
firstfield.focus()
});
</script>
{% endblock %}