aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--TODO2
-rw-r--r--layerindex/urls.py6
-rw-r--r--layerindex/views.py68
-rw-r--r--templates/base.html9
-rw-r--r--templates/layerindex/duplicates.html96
5 files changed, 156 insertions, 25 deletions
diff --git a/TODO b/TODO
index fa01663760..c69996f683 100644
--- a/TODO
+++ b/TODO
@@ -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 %}