diff options
-rw-r--r-- | TODO | 2 | ||||
-rw-r--r-- | layerindex/urls.py | 6 | ||||
-rw-r--r-- | layerindex/views.py | 68 | ||||
-rw-r--r-- | templates/base.html | 9 | ||||
-rw-r--r-- | templates/layerindex/duplicates.html | 96 |
5 files changed, 156 insertions, 25 deletions
@@ -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? diff --git a/layerindex/urls.py b/layerindex/urls.py index 668b7c4291..fc67a4914f 100644 --- a/layerindex/urls.py +++ b/layerindex/urls.py @@ -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'), diff --git a/layerindex/views.py b/layerindex/views.py index fe22b580c8..023ffc2dfc 100644 --- a/layerindex/views.py +++ b/layerindex/views.py @@ -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,6 +256,28 @@ class LayerReviewDetailView(LayerDetailView): raise PermissionDenied return super(LayerReviewDetailView, self).dispatch(request, *args, **kwargs) +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( + select={ + 'preferred_count': """SELECT COUNT(1) +FROM layerindex_recipe AS recipe2 +, layerindex_layerbranch as branch2 +, layerindex_layeritem as layer1 +, layerindex_layeritem as layer2 +WHERE branch2.id = recipe2.layerbranch_id +AND layer2.id = branch2.layer_id +AND layer2.layer_type in ('S', 'A') +AND branch2.branch_id = layerindex_layerbranch.branch_id +AND recipe2.pn = layerindex_recipe.pn +AND recipe2.layerbranch_id <> layerindex_recipe.layerbranch_id +AND layer1.id = layerindex_layerbranch.layer_id +AND layer2.index_preference > layer1.index_preference +""" + }, + ) + class RecipeSearchView(ListView): context_object_name = 'recipe_list' paginate_by = 50 @@ -275,32 +297,32 @@ class RecipeSearchView(ListView): # with no query string) return Recipe.objects.none() - # 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( - select={ - 'preferred_count': """SELECT COUNT(1) -FROM layerindex_recipe AS recipe2 -, layerindex_layerbranch as branch2 -, layerindex_layeritem as layer1 -, layerindex_layeritem as layer2 -WHERE branch2.id = recipe2.layerbranch_id -AND layer2.id = branch2.layer_id -AND layer2.layer_type in ('S', 'A') -AND branch2.branch_id = layerindex_layerbranch.branch_id -AND recipe2.pn = layerindex_recipe.pn -AND recipe2.layerbranch_id <> layerindex_recipe.layerbranch_id -AND layer1.id = layerindex_layerbranch.layer_id -AND layer2.index_preference > layer1.index_preference -""" - }, - ) + 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 diff --git a/templates/base.html b/templates/base.html index fd591ad623..f11674e352 100644 --- a/templates/base.html +++ b/templates/base.html @@ -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"> diff --git a/templates/layerindex/duplicates.html b/templates/layerindex/duplicates.html new file mode 100644 index 0000000000..c6d06b2195 --- /dev/null +++ b/templates/layerindex/duplicates.html @@ -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 %} |