Implement package difference filtering

This is done as client-side JS which makes the page nice and fast. Minor
versions can be excluded, as can packages in [multilib]. In addition,
architecture filtering is in place so you can limit the subset of shown
packages to those in any, both, one or the other.

Signed-off-by: Dan McGee <dan@archlinux.org>
This commit is contained in:
Dan McGee 2010-09-07 16:04:56 -05:00
parent f498ceca1d
commit 9df541f95f
4 changed files with 117 additions and 23 deletions

View File

@ -175,7 +175,6 @@ table.results th.headerSortUp { background-color: #e4eeff; background-image: url
table.results .flagged { color: red; }
/* pkglist: layout */
div#pkglist-search { margin-bottom: 1.5em; }
div#pkglist-about { margin-top: 1.5em; }
/* pkglist: results navigation */
@ -184,11 +183,11 @@ div#pkglist-about { margin-top: 1.5em; }
.pkglist-nav .prev { margin-right: 1em; }
.pkglist-nav .next { margin-right: 1em; }
/* pkglist: search fields */
#pkglist-search h3 { font-size: 1em; margin-top:0; }
#pkglist-search div { float: left; margin-right: 1.65em; font-size: 0.85em; }
#pkglist-search legend { display: none; }
#pkglist-search label { width: auto; display: block; font-weight: normal; }
/* search fields and other filter selections */
.filter-criteria h3 { font-size: 1em; margin-top:0; }
.filter-criteria div { float: left; margin-right: 1.65em; font-size: 0.85em; }
.filter-criteria legend { display: none; }
.filter-criteria label { width: auto; display: block; font-weight: normal; }
/* pkgdetails: details links that float on the right */
#pkgdetails #detailslinks { float: right; }

View File

@ -44,6 +44,32 @@ def get_group_info():
groups.extend(val.itervalues())
return sorted(groups, key=itemgetter('name', 'arch'))
class Difference(object):
def __init__(self, pkgname, repo, pkg_a, pkg_b):
self.pkgname = pkgname
self.repo = repo
self.pkg_a = pkg_a
self.pkg_b = pkg_b
def classes(self):
'''A list of CSS classes that should be applied to this row in any
generated HTML. Useful for sorting, filtering, etc. Contains whether
this difference is in both architectures or the sole architecture it
belongs to, as well as the repo name.'''
css_classes = [self.repo.name.lower()]
if self.pkg_a and self.pkg_b:
css_classes.append('both')
elif self.pkg_a:
css_classes.append(self.pkg_a.arch.name)
elif self.pkg_b:
css_classes.append(self.pkg_b.arch.name)
return ' '.join(css_classes)
def __cmp__(self, other):
if isinstance(other, Difference):
return cmp(self.__dict__, other.__dict__)
return False
@cache_function(300)
def get_differences_info(arch_a, arch_b):
# This is a monster. Join packages against itself, looking for packages in
@ -87,17 +113,17 @@ def get_differences_info(arch_a, arch_b):
# We want arch_a to always appear first
# pkg_a should never be None
if pkg_a.arch == arch_a:
item = (pkg_a.pkgname, pkg_a.repo, pkg_a, pkg_b)
item = Difference(pkg_a.pkgname, pkg_a.repo, pkg_a, pkg_b)
else:
# pkg_b can be None in this case, so be careful
name = pkg_a.pkgname if pkg_a else pkg_b.pkgname
repo = pkg_a.repo if pkg_a else pkg_b.repo
item = (name, repo, pkg_b, pkg_a)
item = Difference(name, repo, pkg_b, pkg_a)
if item not in differences:
differences.append(item)
# now sort our list by repository, package name
differences.sort(key=lambda a: (a[1].name, a[0]))
differences.sort(key=lambda a: (a.repo.name, a.pkgname))
return differences
# vim: set ts=4 sw=4 et:

View File

@ -51,6 +51,8 @@
'django.template.loaders.app_directories.load_template_source',
)
# This bug is a real bummer:
# http://code.djangoproject.com/ticket/14105
MIDDLEWARE_CLASSES = (
'main.middleware.UpdateCacheMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',

View File

@ -4,8 +4,30 @@
{% block content %}
{% if differences %}
<div class="box">
<div id="differences-filter" class="box filter-criteria">
<h2>Package Differences by Architecture</h2>
<h3>Filter Differences View</h3>
<form id="diff_filter" method="post" action=".">
<fieldset>
<legend>Select filter criteria</legend>
<div><label for="id_archonly" title="Limit packages to selected architecture">Architecture Limitation</label>
<select name="archonly" id="id_archonly">
<option value="all">Show All</option>
<option value="both">Only In Both</option>
<option value="{{ arch_a.name }}">In {{ arch_a.name }} Only</option>
<option value="{{ arch_b.name }}">In {{ arch_b.name }} Only</option>
</select>
</div>
<div><label for="id_multilib" title="Show multilib packages"><tt>[multilib]</tt> Visible</label>
<input type="checkbox" checked="checked" name="multilib" id="id_multilib" value="multilib"/></div>
<div><label for="id_minor" title="Show minor version mismatches">Minor Version Mismatches</label>
<input type="checkbox" checked="checked" name="minor" id="id_minor" value="minor"/></div>
<div ><label>&nbsp;</label><input title="Reset search criteria" type="button" id="criteria_reset" value="Reset"/></div>
</fieldset>
</form>
</div>
<div class="box">
<table class="results">
<thead>
<tr>
@ -16,19 +38,19 @@ <h2>Package Differences by Architecture</h2>
</tr>
</thead>
<tbody>
{% for name, repo, pkg1, pkg2 in differences %}
<tr class="{% cycle 'odd' 'even' %}">
<td>{{ name }}</td>
<td>{{ repo.name }}</td>
{% if pkg1 %}
<td><a href="{{ pkg1.get_absolute_url }}"
title="View package details for {{ pkg1.pkgname }}">
<span{% if pkg1.flag_date %} class="flagged"{% endif %}>{{ pkg1.pkgver }}-{{ pkg1.pkgrel }}</span></a></td>
{% for diff in differences %}
<tr class="{% cycle 'odd' 'even' %} {{ diff.classes }}">
<td>{{ diff.pkgname }}</td>
<td>{{ diff.repo.name }}</td>
{% if diff.pkg_a %}
<td><a href="{{ diff.pkg_a.get_absolute_url }}"
title="View package details for {{ diff.pkg_a.pkgname }}">
<span{% if diff.pkg_a.flag_date %} class="flagged"{% endif %}>{{ diff.pkg_a.pkgver }}-{{ diff.pkg_a.pkgrel }}</span></a></td>
{% else %}<td>-</td>{% endif %}
{% if pkg2 %}
<td><a href="{{ pkg2.get_absolute_url }}"
title="View package details for {{ pkg2.pkgname }}">
<span{% if pkg2.flag_date %} class="flagged"{% endif %}>{{ pkg2.pkgver }}-{{ pkg2.pkgrel }}</span></a></td>
{% if diff.pkg_b %}
<td><a href="{{ diff.pkg_b.get_absolute_url }}"
title="View package details for {{ diff.pkg_b.pkgname }}">
<span{% if diff.pkg_b.flag_date %} class="flagged"{% endif %}>{{ diff.pkg_b.pkgver }}-{{ diff.pkg_b.pkgrel }}</span></a></td>
{% else %}<td>-</td>{% endif %}
</tr>
{% endfor %}
@ -38,8 +60,53 @@ <h2>Package Differences by Architecture</h2>
{% load cdn %}{% jquery %}
<script type="text/javascript" src="/media/jquery.tablesorter.min.js"></script>
<script type="text/javascript">
filter_packages = function() {
// start with all rows, and then remove ones we shouldn't show
var rows = $(".results tbody tr");
if(!$('#id_multilib').is(':checked')) {
rows = rows.not(".multilib");
}
var arch = $("#id_archonly").val();
if(arch !== "all") {
rows = rows.filter("." + arch);
}
if(!$('#id_minor').is(':checked')) {
// this check is done last because it is the most expensive
rows = rows.filter(function(index) {
// all this just to get the split version out of the table cell
var pat = /(.*)-(.+)/;
var ver_a = $('td:eq(2) a', this).text().match(pat);
var ver_b = $('td:eq(3) a', this).text().match(pat);
// did we match at all?
if(!ver_a || !ver_b) return true;
// first check pkgver
if(ver_a[1] !== ver_b[1]) return true;
// pkgver matched, so see if rounded pkgrel matches
if(Math.floor(parseFloat(ver_a[1])) == Math.floor(parseFloat(ver_b[1]))) return false;
// pkgrel didn't match, so keep the row
return true;
});
}
// hide all rows, then show the set we care about
$('.results tbody tr').hide();
rows.show();
// make sure we update the odd/even styling from sorting
$('.results').trigger("applyWidgets");
};
filter_reset = function() {
console.log("reset firing");
$('#id_archonly').val("all");
$('#id_multilib').attr("checked", "checked");
$('#id_minor').attr("checked", "checked");
filter_packages();
};
$(document).ready(function() {
$(".results").tablesorter({widgets: ['zebra'], sortList: [[1,0], [0,0]]});
$('.results').tablesorter({widgets: ['zebra'], sortList: [[1,0], [0,0]]});
$('#diff_filter select').change(filter_packages);
$('#diff_filter input').change(filter_packages);
$('#criteria_reset').click(filter_reset);
// fire function on page load to ensure the current form selections take effect
filter_packages();
});
</script>
{% endif %}