mirror of
git://git.yoctoproject.org/layerindex-web.git
synced 2025-07-19 20:59:01 +02:00
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:
parent
9af1144928
commit
89f9e6afe8
2
TODO
2
TODO
|
@ -20,7 +20,7 @@ Later:
|
||||||
* Return to last page (review/detail) after editing (with success alert)?
|
* Return to last page (review/detail) after editing (with success alert)?
|
||||||
* Cancel button on edit form?
|
* Cancel button on edit form?
|
||||||
* Query backend service? i.e. special URL to query information for external apps/scripts
|
* 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]
|
* Tool for editing SUMMARY/DESCRIPTION? [Paul working on this]
|
||||||
* Dynamic loading/filtering for recipes list
|
* Dynamic loading/filtering for recipes list
|
||||||
* Some way to notify the user when they search for something that has been renamed / replaced / deprecated?
|
* Some way to notify the user when they search for something that has been renamed / replaced / deprecated?
|
||||||
|
|
|
@ -8,7 +8,7 @@ from django.conf.urls.defaults import *
|
||||||
from django.views.generic import TemplateView, DetailView, ListView
|
from django.views.generic import TemplateView, DetailView, ListView
|
||||||
from django.views.defaults import page_not_found
|
from django.views.defaults import page_not_found
|
||||||
from layerindex.models import LayerItem, Recipe
|
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('',
|
urlpatterns = patterns('',
|
||||||
url(r'^$',
|
url(r'^$',
|
||||||
|
@ -71,6 +71,10 @@ urlpatterns = patterns('',
|
||||||
HistoryListView.as_view(
|
HistoryListView.as_view(
|
||||||
template_name='layerindex/history.html'),
|
template_name='layerindex/history.html'),
|
||||||
name='history_list'),
|
name='history_list'),
|
||||||
|
url(r'^duplicates/$',
|
||||||
|
DuplicatesView.as_view(
|
||||||
|
template_name='layerindex/duplicates.html'),
|
||||||
|
name='duplicates'),
|
||||||
url(r'^profile/$',
|
url(r'^profile/$',
|
||||||
EditProfileFormView.as_view(
|
EditProfileFormView.as_view(
|
||||||
template_name='layerindex/profile.html'),
|
template_name='layerindex/profile.html'),
|
||||||
|
|
|
@ -9,14 +9,14 @@ from django.http import HttpResponse, HttpResponseRedirect, HttpResponseForbidde
|
||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.core.exceptions import PermissionDenied
|
from django.core.exceptions import PermissionDenied
|
||||||
from django.template import RequestContext
|
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 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 django.views.generic.edit import UpdateView
|
||||||
from layerindex.forms import EditLayerForm, LayerMaintainerFormSet, EditNoteForm, EditProfileForm
|
from layerindex.forms import EditLayerForm, LayerMaintainerFormSet, EditNoteForm, EditProfileForm
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.contrib.auth.models import User, Permission
|
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.core.mail import EmailMessage
|
||||||
from django.template.loader import get_template
|
from django.template.loader import get_template
|
||||||
from django.template import Context
|
from django.template import Context
|
||||||
|
@ -256,6 +256,28 @@ class LayerReviewDetailView(LayerDetailView):
|
||||||
raise PermissionDenied
|
raise PermissionDenied
|
||||||
return super(LayerReviewDetailView, self).dispatch(request, *args, **kwargs)
|
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):
|
class RecipeSearchView(ListView):
|
||||||
context_object_name = 'recipe_list'
|
context_object_name = 'recipe_list'
|
||||||
paginate_by = 50
|
paginate_by = 50
|
||||||
|
@ -275,32 +297,32 @@ class RecipeSearchView(ListView):
|
||||||
# with no query string)
|
# with no query string)
|
||||||
return Recipe.objects.none()
|
return Recipe.objects.none()
|
||||||
|
|
||||||
# Add extra column so we can show "duplicate" recipes from other layers de-emphasised
|
return recipes_preferred_count(qs)
|
||||||
# (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
|
|
||||||
"""
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super(RecipeSearchView, self).get_context_data(**kwargs)
|
context = super(RecipeSearchView, self).get_context_data(**kwargs)
|
||||||
context['search_keyword'] = self.request.GET.get('q', '')
|
context['search_keyword'] = self.request.GET.get('q', '')
|
||||||
return context
|
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):
|
class MachineSearchView(ListView):
|
||||||
context_object_name = 'machine_list'
|
context_object_name = 'machine_list'
|
||||||
paginate_by = 50
|
paginate_by = 50
|
||||||
|
|
|
@ -63,6 +63,15 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</a></li>
|
</a></li>
|
||||||
{% endif %}
|
{% 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="divider-vertical"></li>
|
||||||
<li class="dropdown">
|
<li class="dropdown">
|
||||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
|
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
|
||||||
|
|
96
templates/layerindex/duplicates.html
Normal file
96
templates/layerindex/duplicates.html
Normal 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 %}
|
Loading…
Reference in New Issue
Block a user