summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke Shumaker <LukeShu@sbcglobal.net>2013-04-23 21:57:59 -0400
committerLuke Shumaker <LukeShu@sbcglobal.net>2013-04-23 21:57:59 -0400
commit55b776d58999412cd6bf0787a41d6ab00bf80fb6 (patch)
treee6c151ee22207465346b230f92a42d75576409d1
parent4578eb77da75c24f9b366c2ade46f55b0702b340 (diff)
parentac1c00ee86cc0e355af5e4e6be47ca861091356b (diff)
Merge branch 'archweb' into archweb-generic2
-rw-r--r--devel/urls.py1
-rw-r--r--devel/views.py21
-rw-r--r--main/admin.py2
-rw-r--r--main/fixtures/arches.json49
-rw-r--r--main/migrations/0064_auto__add_field_arch_required_signoffs.py112
-rw-r--r--main/models.py2
-rw-r--r--packages/management/commands/populate_signoffs.py6
-rw-r--r--packages/models.py12
-rw-r--r--packages/utils.py7
-rw-r--r--sitestatic/archweb.js16
-rw-r--r--templates/devel/index.html148
-rw-r--r--templates/devel/packages.html2
-rw-r--r--templates/devel/stats.html120
-rw-r--r--templates/packages/signoffs.html2
14 files changed, 331 insertions, 169 deletions
diff --git a/devel/urls.py b/devel/urls.py
index 31afc86b..472c6456 100644
--- a/devel/urls.py
+++ b/devel/urls.py
@@ -5,6 +5,7 @@ urlpatterns = patterns('devel.views',
(r'^admin_log/(?P<username>.*)/$','admin_log'),
(r'^clock/$', 'clock', {}, 'devel-clocks'),
(r'^$', 'index', {}, 'devel-index'),
+ (r'^stats/$', 'stats', {}, 'devel-stats'),
(r'^newuser/$', 'new_user_form'),
(r'^profile/$', 'change_profile'),
(r'^reports/(?P<report_name>.*)/(?P<username>.*)/$', 'report'),
diff --git a/devel/views.py b/devel/views.py
index 4258ea7f..378d6d57 100644
--- a/devel/views.py
+++ b/devel/views.py
@@ -33,7 +33,7 @@ from .utils import get_annotated_maintainers
@login_required
def index(request):
- '''the developer dashboard'''
+ """The developer dashboard."""
if request.user.is_authenticated():
inner_q = PackageRelation.objects.filter(user=request.user)
else:
@@ -54,6 +54,19 @@ def index(request):
signoffs = sorted(get_signoff_groups(user=request.user),
key=operator.attrgetter('pkgbase'))
+ page_dict = {
+ 'todos': todolists,
+ 'flagged': flagged,
+ 'todopkgs': todopkgs,
+ 'signoffs': signoffs
+ }
+
+ return render(request, 'devel/index.html', page_dict)
+
+
+@login_required
+def stats(request):
+ """The second half of the dev dashboard."""
arches = Arch.objects.all().annotate(
total_ct=Count('packages'), flagged_ct=Count('packages__flag_date'))
repos = Repo.objects.all().annotate(
@@ -80,17 +93,13 @@ def index(request):
}
page_dict = {
- 'todos': todolists,
'arches': arches,
'repos': repos,
'maintainers': maintainers,
'orphan': orphan,
- 'flagged': flagged,
- 'todopkgs': todopkgs,
- 'signoffs': signoffs
}
- return render(request, 'devel/index.html', page_dict)
+ return render(request, 'devel/stats.html', page_dict)
@login_required
diff --git a/main/admin.py b/main/admin.py
index ef134187..6aff12e5 100644
--- a/main/admin.py
+++ b/main/admin.py
@@ -8,7 +8,7 @@ class DonorAdmin(admin.ModelAdmin):
exclude = ('created',)
class ArchAdmin(admin.ModelAdmin):
- list_display = ('name', 'agnostic')
+ list_display = ('name', 'agnostic', 'required_signoffs')
list_filter = ('agnostic',)
search_fields = ('name',)
diff --git a/main/fixtures/arches.json b/main/fixtures/arches.json
index 6334c2d3..1ece16c9 100644
--- a/main/fixtures/arches.json
+++ b/main/fixtures/arches.json
@@ -1,26 +1,29 @@
[
- {
- "pk": 1,
- "model": "main.arch",
- "fields": {
- "agnostic": true,
- "name": "any"
- }
- },
- {
- "pk": 2,
- "model": "main.arch",
- "fields": {
- "agnostic": false,
- "name": "i686"
- }
- },
- {
- "pk": 3,
- "model": "main.arch",
- "fields": {
- "agnostic": false,
- "name": "x86_64"
- }
+{
+ "pk": 1,
+ "model": "main.arch",
+ "fields": {
+ "agnostic": true,
+ "name": "any",
+ "required_signoffs": 2
}
+},
+{
+ "pk": 2,
+ "model": "main.arch",
+ "fields": {
+ "agnostic": false,
+ "name": "i686",
+ "required_signoffs": 1
+ }
+},
+{
+ "pk": 3,
+ "model": "main.arch",
+ "fields": {
+ "agnostic": false,
+ "name": "x86_64",
+ "required_signoffs": 2
+ }
+}
]
diff --git a/main/migrations/0064_auto__add_field_arch_required_signoffs.py b/main/migrations/0064_auto__add_field_arch_required_signoffs.py
new file mode 100644
index 00000000..1846378f
--- /dev/null
+++ b/main/migrations/0064_auto__add_field_arch_required_signoffs.py
@@ -0,0 +1,112 @@
+# -*- coding: utf-8 -*-
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+ db.add_column('arches', 'required_signoffs',
+ self.gf('django.db.models.fields.PositiveIntegerField')(default=2),
+ keep_default=True)
+
+ def backwards(self, orm):
+ db.delete_column('arches', 'required_signoffs')
+
+ models = {
+ u'auth.group': {
+ 'Meta': {'object_name': 'Group'},
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+ },
+ u'auth.permission': {
+ 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ u'auth.user': {
+ 'Meta': {'object_name': 'User'},
+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+ },
+ u'contenttypes.contenttype': {
+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ u'main.arch': {
+ 'Meta': {'ordering': "('name',)", 'object_name': 'Arch', 'db_table': "'arches'"},
+ 'agnostic': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+ 'required_signoffs': ('django.db.models.fields.PositiveIntegerField', [], {'default': '2'})
+ },
+ u'main.donor': {
+ 'Meta': {'ordering': "('name',)", 'object_name': 'Donor', 'db_table': "'donors'"},
+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+ 'visible': ('django.db.models.fields.BooleanField', [], {'default': 'True'})
+ },
+ u'main.package': {
+ 'Meta': {'ordering': "('pkgname',)", 'unique_together': "(('pkgname', 'repo', 'arch'),)", 'object_name': 'Package', 'db_table': "'packages'"},
+ 'arch': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'packages'", 'on_delete': 'models.PROTECT', 'to': u"orm['main.Arch']"}),
+ 'build_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
+ 'compressed_size': ('main.fields.PositiveBigIntegerField', [], {}),
+ 'created': ('django.db.models.fields.DateTimeField', [], {}),
+ 'epoch': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
+ 'filename': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'files_last_update': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ 'flag_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'installed_size': ('main.fields.PositiveBigIntegerField', [], {}),
+ 'last_update': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
+ 'packager': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
+ 'packager_str': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'pgp_signature': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+ 'pkgbase': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
+ 'pkgdesc': ('django.db.models.fields.TextField', [], {'null': 'True'}),
+ 'pkgname': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'pkgrel': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'pkgver': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'repo': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'packages'", 'on_delete': 'models.PROTECT', 'to': u"orm['main.Repo']"}),
+ 'url': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'})
+ },
+ u'main.packagefile': {
+ 'Meta': {'object_name': 'PackageFile', 'db_table': "'package_files'"},
+ 'directory': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'filename': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_directory': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'pkg': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['main.Package']"})
+ },
+ u'main.repo': {
+ 'Meta': {'ordering': "('name',)", 'object_name': 'Repo', 'db_table': "'repos'"},
+ 'bugs_category': ('django.db.models.fields.SmallIntegerField', [], {'default': '2'}),
+ 'bugs_project': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'}),
+ u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+ 'staging': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'svn_root': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
+ 'testing': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
+ }
+ }
+
+ complete_apps = ['main']
diff --git a/main/models.py b/main/models.py
index 2f4d3520..2ace0109 100644
--- a/main/models.py
+++ b/main/models.py
@@ -48,6 +48,8 @@ class Arch(models.Model):
name = models.CharField(max_length=255, unique=True)
agnostic = models.BooleanField(default=False,
help_text="Is this architecture non-platform specific?")
+ required_signoffs = models.PositiveIntegerField(default=2,
+ help_text="Number of signoffs required for packages of this architecture")
def __unicode__(self):
return self.name
diff --git a/packages/management/commands/populate_signoffs.py b/packages/management/commands/populate_signoffs.py
index 97ba4146..a9c1c81c 100644
--- a/packages/management/commands/populate_signoffs.py
+++ b/packages/management/commands/populate_signoffs.py
@@ -29,7 +29,8 @@ logging.basicConfig(
logger = logging.getLogger()
class Command(NoArgsCommand):
- help = "Pull the latest commit message from SVN for a given package that is signoff-eligible and does not have an existing comment attached"
+ help = """Pull the latest commit message from SVN for a given package that
+is signoff-eligible and does not have an existing comment attached"""
def handle_noargs(self, **options):
v = int(options.get('verbosity', None))
@@ -74,10 +75,11 @@ cached_svn_log.cache = {}
def create_specification(package, log, finder):
trimmed_message = log['message'].strip()
+ required = package.arch.required_signoffs
spec = SignoffSpecification(pkgbase=package.pkgbase,
pkgver=package.pkgver, pkgrel=package.pkgrel,
epoch=package.epoch, arch=package.arch, repo=package.repo,
- comments=trimmed_message)
+ comments=trimmed_message, required=required)
spec.user = finder.find_by_username(log['author'])
return spec
diff --git a/packages/models.py b/packages/models.py
index f830aade..6477d412 100644
--- a/packages/models.py
+++ b/packages/models.py
@@ -60,7 +60,8 @@ class SignoffSpecificationManager(models.Manager):
pkgbase=pkg.pkgbase, pkgver=pkg.pkgver, pkgrel=pkg.pkgrel,
epoch=pkg.epoch, arch=pkg.arch, repo=pkg.repo)
except SignoffSpecification.DoesNotExist:
- return DEFAULT_SIGNOFF_SPEC
+ return fake_signoff_spec(pkg.arch)
+
class SignoffSpecification(models.Model):
'''
@@ -97,10 +98,15 @@ class SignoffSpecification(models.Model):
return u'%s-%s' % (self.pkgbase, self.full_version)
-# fake default signoff spec when we don't have a persisted one in the database
+# Fake signoff specs for when we don't have persisted ones in the database.
+# These have all necessary attributes of the real thing but are lighter weight
+# and have no chance of being persisted.
FakeSignoffSpecification = namedtuple('FakeSignoffSpecification',
('required', 'enabled', 'known_bad', 'comments'))
-DEFAULT_SIGNOFF_SPEC = FakeSignoffSpecification(2, True, False, u'')
+
+
+def fake_signoff_spec(arch):
+ return FakeSignoffSpecification(arch.required_signoffs, True, False, u'')
class SignoffManager(models.Manager):
diff --git a/packages/utils.py b/packages/utils.py
index 4f3b8665..6ec39483 100644
--- a/packages/utils.py
+++ b/packages/utils.py
@@ -14,7 +14,7 @@ from main.utils import (database_vendor,
groupby_preserve_order, PackageStandin)
from .models import (PackageGroup, PackageRelation,
License, Depend, Conflict, Provision, Replacement,
- SignoffSpecification, Signoff, DEFAULT_SIGNOFF_SPEC)
+ SignoffSpecification, Signoff, fake_signoff_spec)
VERSION_RE = re.compile(r'^((\d+):)?(.+)-([^-]+)$')
@@ -297,7 +297,6 @@ class PackageSignoffGroup(object):
self.user = None
self.target_repo = None
self.signoffs = set()
- self.specification = DEFAULT_SIGNOFF_SPEC
self.default_spec = True
first = packages[0]
@@ -308,6 +307,7 @@ class PackageSignoffGroup(object):
self.last_update = first.last_update
self.packager = first.packager
self.maintainers = first.maintainers
+ self.specification = fake_signoff_spec(first.arch)
version = first.full_version
if all(version == pkg.full_version for pkg in packages):
@@ -410,7 +410,8 @@ def get_current_signoffs(repos):
def get_current_specifications(repos):
'''Returns a list of signoff specification objects for the given repos.'''
to_fetch = signoffs_id_query(SignoffSpecification, repos)
- return SignoffSpecification.objects.in_bulk(to_fetch).values()
+ return SignoffSpecification.objects.select_related('arch').in_bulk(
+ to_fetch).values()
def get_target_repo_map(repos):
diff --git a/sitestatic/archweb.js b/sitestatic/archweb.js
index f7be50d8..be6f5256 100644
--- a/sitestatic/archweb.js
+++ b/sitestatic/archweb.js
@@ -31,6 +31,7 @@ if (typeof $ !== 'undefined' && typeof $.tablesorter !== 'undefined') {
},
type: 'numeric'
});
+
$.tablesorter.addParser({
id: 'todostatus',
is: function(s) { return false; },
@@ -44,6 +45,7 @@ if (typeof $ !== 'undefined' && typeof $.tablesorter !== 'undefined') {
},
type: 'numeric'
});
+
$.tablesorter.addParser({
/* sorts numeric, but put '', 'unknown', and '∞' last. */
id: 'mostlydigit',
@@ -60,6 +62,7 @@ if (typeof $ !== 'undefined' && typeof $.tablesorter !== 'undefined') {
},
type: 'numeric'
});
+
$.tablesorter.addParser({
/* sorts duration; put '', 'unknown', and '∞' last. */
id: 'duration',
@@ -93,6 +96,7 @@ if (typeof $ !== 'undefined' && typeof $.tablesorter !== 'undefined') {
},
type: 'numeric'
});
+
$.tablesorter.addParser({
id: 'longDateTime',
re: /^(\d{4})-(\d{2})-(\d{2}) ([012]\d):([0-5]\d)(:([0-5]\d))?( (\w+))?$/,
@@ -116,6 +120,7 @@ if (typeof $ !== 'undefined' && typeof $.tablesorter !== 'undefined') {
},
type: 'numeric'
});
+
$.tablesorter.addParser({
id: 'filesize',
re: /^(\d+(?:\.\d+)?) (bytes?|[KMGTPEZY]i?B)$/,
@@ -161,6 +166,17 @@ if (typeof $ !== 'undefined' && typeof $.tablesorter !== 'undefined') {
},
type: 'numeric'
});
+
+ $.tablesorter.removeParser = function(id) {
+ $.tablesorter.parsers = $.grep($.tablesorter.parsers,
+ function(ele, i) {
+ return ele.id !== id;
+ });
+ };
+
+ // We don't use currency, and the parser is over-zealous at deciding it
+ // matches. Kill it from the parser selection.
+ $.tablesorter.removeParser('currency');
}
(function($) {
diff --git a/templates/devel/index.html b/templates/devel/index.html
index f3f32863..62cdc1ed 100644
--- a/templates/devel/index.html
+++ b/templates/devel/index.html
@@ -161,7 +161,8 @@
Self-explanatory
(<a href="reports/uncompressed-info/{{ user.username }}/">yours only</a>)</li>
<li><a href="reports/mismatched-signature/">Mismatched Signatures</a>:
- Packages where 1) signing key is unknown, 2) signer != packager, or 3) signature timestamp more than 24 hours after build timestamp
+ Packages where 1) signing key is unknown, 2) signer != packager,
+ or 3) signature timestamp more than 24 hours after build timestamp
(<a href="reports/mismatched-signature/{{ user.username }}/">yours only</a>)</li>
<li><a href="reports/big/">Big</a>:
All packages with compressed size &gt; 50 MiB
@@ -173,127 +174,11 @@
Packages that have no maintainer and are not required by any other
package in any repository</li>
</ul>
+</div>{# #dev-dashboard #}
-</div><!-- #dev-dashboard -->
-
-{% cache 60 dev-dash-by-arch %}
-<div id="dash-by-arch" class="box">
-
- <h2>Stats by Architecture</h2>
-
- <table id="stats-by-arch" class="results dash-stats">
- <thead>
- <tr>
- <th class="key">Arch</th>
- <th># Packages</th>
- <th># Flagged</th>
- </tr>
- </thead>
- <tbody>
- {% for arch in arches %}
- <tr class="{% cycle 'odd' 'even' %}">
- <td>{{ arch.name }}</td>
- <td><a href="/packages/?arch={{ arch.name }}"
- title="View all packages for the {{ arch.name }} architecture">
- <strong>{{ arch.total_ct }}</strong> packages</a></td>
- <td><a href="/packages/?arch={{ arch.name }}&amp;flagged=Flagged"
- title="View all flagged packages for the {{ arch.name }} architecture">
- <strong>{{ arch.flagged_ct }}</strong> packages</a></td>
- </tr>
- {% endfor %}
- </tbody>
- </table>
-</div>{# #dash-by-arch #}
-{% endcache %}
-
-{% cache 60 dev-dash-by-repo %}
-<div id="dash-by-repo" class="box">
-
- <h2>Stats by Repository</h2>
-
- <table id="stats-by-repo" class="results dash-stats">
- <thead>
- <tr>
- <th class="key">Repository</th>
- <th># Packages</th>
- <th># Flagged</th>
- <th># Maintainers</th>
- </tr>
- </thead>
- <tbody>
- {% for repo in repos %}
- <tr class="{% cycle 'odd' 'even' %}">
- <td>{{ repo.name }}</td>
- <td><a href="/packages/?repo={{ repo.name }}"
- title="View all packages in the {{ repo.name }} repository">
- <strong>{{ repo.total_ct }}</strong> packages</a></td>
- <td><a href="/packages/?repo={{ repo.name }}&amp;flagged=Flagged"
- title="View all flagged packages in the {{ repo.name }} repository">
- <strong>{{ repo.flagged_ct }}</strong> packages</a></td>
- <td><strong>{{ repo.maintainer_ct }}</strong> maintainers</td>
- </tr>
- </tr>
- {% endfor %}
- </tbody>
- </table>
-</div>{# dash-by-arch #}
-{% endcache %}
-
-{% cache 60 dev-dash-by-developer %}
-<div id="dash-by-developer" class="box">
-
- <h2>Stats by Developer</h2>
-
- {% if perms.main.change_package %}
- <p><a href="/packages/stale_relations/">Look for stale relations</a></p>
- {% endif %}
-
- <table id="stats-by-maintainer" class="results dash-stats">
- <thead>
- <tr>
- <th class="key">Maintainer</th>
- <th># Maintained</th>
- <th># Flagged</th>
- <th># Last Packager</th>
- </tr>
- <tr class="even">
- <td><em>Orphan/Unknown</em></td>
- <td><a href="/packages/?maintainer=orphan"
- title="View all orphan packages">
- <strong>{{ orphan.package_count }}</strong> packages</a>
- </td>
- <td><a href="/packages/?maintainer=orphan&amp;flagged=Flagged"
- title="View all flagged orphan packages">
- <strong>{{ orphan.flagged_count }}</strong> packages</a>
- </td>
- <td><a href="/packages/?packager=unknown"
- title="View all packages last updated by unknown">
- <strong>{{ orphan.updated_count }}</strong> packages</a>
- </td>
- </tr>
- </thead>
- <tbody>
- {% for maint in maintainers %}
- <tr class="{% cycle 'odd' 'even' %}">
- <td>{{ maint.get_full_name }}</td>
- <td><a href="/packages/?maintainer={{ maint.username }}"
- title="View all packages maintained by {{ maint.get_full_name }}">
- <strong>{{ maint.package_count }}</strong> packages</a>
- </td>
- <td><a href="/packages/?maintainer={{ maint.username }}&amp;flagged=Flagged"
- title="View all flagged packages maintained by {{ maint.get_full_name }}">
- <strong>{{ maint.flagged_count }}</strong> packages</a>
- </td>
- <td><a href="/packages/?packager={{ maint.username }}"
- title="View all packages last updated by {{ maint.get_full_name }}">
- <strong>{{ maint.updated_count }}</strong> packages</a>
- </td>
- </tr>
- {% endfor %}
- </tbody>
- </table>
-</div>{# #dash-by-developer #}
-{% endcache %}
+<div id='stats-area'>
+ <p>Enable Javascript to get more useful info here.</p>
+</div>
{% endblock %}
{% block script_block %}
@@ -301,6 +186,19 @@
<script type="text/javascript" src="{% static "archweb.js" %}"></script>
<script type="text/javascript">
$(document).ready(function() {
+ $("#stats-area").html('<p>Loading stats…</p>');
+ $("#stats-area").load('stats/', function() {
+ var settings = {
+ widgets: ['zebra'],
+ sortList: [[0,0]],
+ headers: { 1: { sorter: 'pkgcount' }, 2: { sorter: 'pkgcount' }, 3: { sorter: 'pkgcount' } }
+ };
+
+ $(".dash-stats").not($("#stats-by-maintainer")).tablesorter(settings);
+ settings['sortLocaleCompare'] = true;
+ $("#stats-by-maintainer").tablesorter(settings);
+ });
+
$("#dash-myflagged:not(:has(tbody tr.empty))").tablesorter(
{widgets: ['zebra'], sortList: [[0,0]]});
$("#dash-mytodolist:not(:has(tbody tr.empty))").tablesorter(
@@ -312,14 +210,6 @@ $(document).ready(function() {
sortList: [[0,0]],
headers: { 6: {sorter: false } }
});
- var settings = {
- widgets: ['zebra'],
- sortList: [[0,0]],
- headers: { 1: { sorter: 'pkgcount' }, 2: { sorter: 'pkgcount' }, 3: { sorter: 'pkgcount' } }
- };
- $(".dash-stats").not($("#stats-by-maintainer")).tablesorter(settings);
- settings['sortLocaleCompare'] = true;
- $("#stats-by-maintainer").tablesorter(settings);
});
</script>
{% endblock %}
diff --git a/templates/devel/packages.html b/templates/devel/packages.html
index ed96ad5a..51e98579 100644
--- a/templates/devel/packages.html
+++ b/templates/devel/packages.html
@@ -86,7 +86,7 @@ $(document).ready(function() {
var filter_func = function() { filter_pkgs_list('#report_filter', '#dev-report-results tbody'); };
$('#report_filter input').change(filter_func);
$('#criteria_reset').click(function() { filter_pkgs_reset(filter_func); });
- // fire function on page load to ensure the current form selections take effect
+ // run on page load to ensure current form selections take effect
filter_func();
});
</script>
diff --git a/templates/devel/stats.html b/templates/devel/stats.html
new file mode 100644
index 00000000..2dbe4755
--- /dev/null
+++ b/templates/devel/stats.html
@@ -0,0 +1,120 @@
+{% load cache %}
+
+{% cache 60 dev-dash-by-arch %}
+<div id="dash-by-arch" class="box">
+
+ <h2>Stats by Architecture</h2>
+
+ <table id="stats-by-arch" class="results dash-stats">
+ <thead>
+ <tr>
+ <th class="key">Arch</th>
+ <th># Packages</th>
+ <th># Flagged</th>
+ </tr>
+ </thead>
+ <tbody>
+ {% for arch in arches %}
+ <tr class="{% cycle 'odd' 'even' %}">
+ <td>{{ arch.name }}</td>
+ <td><a href="/packages/?arch={{ arch.name }}"
+ title="View all packages for the {{ arch.name }} architecture">
+ <strong>{{ arch.total_ct }}</strong> packages</a></td>
+ <td><a href="/packages/?arch={{ arch.name }}&amp;flagged=Flagged"
+ title="View all flagged packages for the {{ arch.name }} architecture">
+ <strong>{{ arch.flagged_ct }}</strong> packages</a></td>
+ </tr>
+ {% endfor %}
+ </tbody>
+ </table>
+</div>{# #dash-by-arch #}
+{% endcache %}
+
+{% cache 60 dev-dash-by-repo %}
+<div id="dash-by-repo" class="box">
+
+ <h2>Stats by Repository</h2>
+
+ <table id="stats-by-repo" class="results dash-stats">
+ <thead>
+ <tr>
+ <th class="key">Repository</th>
+ <th># Packages</th>
+ <th># Flagged</th>
+ <th># Maintainers</th>
+ </tr>
+ </thead>
+ <tbody>
+ {% for repo in repos %}
+ <tr class="{% cycle 'odd' 'even' %}">
+ <td>{{ repo.name }}</td>
+ <td><a href="/packages/?repo={{ repo.name }}"
+ title="View all packages in the {{ repo.name }} repository">
+ <strong>{{ repo.total_ct }}</strong> packages</a></td>
+ <td><a href="/packages/?repo={{ repo.name }}&amp;flagged=Flagged"
+ title="View all flagged packages in the {{ repo.name }} repository">
+ <strong>{{ repo.flagged_ct }}</strong> packages</a></td>
+ <td><strong>{{ repo.maintainer_ct }}</strong> maintainers</td>
+ </tr>
+ </tr>
+ {% endfor %}
+ </tbody>
+ </table>
+</div>{# dash-by-arch #}
+{% endcache %}
+
+{% cache 60 dev-dash-by-developer %}
+<div id="dash-by-developer" class="box">
+
+ <h2>Stats by Developer</h2>
+
+ {% if perms.main.change_package %}
+ <p><a href="/packages/stale_relations/">Look for stale relations</a></p>
+ {% endif %}
+
+ <table id="stats-by-maintainer" class="results dash-stats">
+ <thead>
+ <tr>
+ <th class="key">Maintainer</th>
+ <th># Maintained</th>
+ <th># Flagged</th>
+ <th># Last Packager</th>
+ </tr>
+ <tr class="even">
+ <td><em>Orphan/Unknown</em></td>
+ <td><a href="/packages/?maintainer=orphan"
+ title="View all orphan packages">
+ <strong>{{ orphan.package_count }}</strong> packages</a>
+ </td>
+ <td><a href="/packages/?maintainer=orphan&amp;flagged=Flagged"
+ title="View all flagged orphan packages">
+ <strong>{{ orphan.flagged_count }}</strong> packages</a>
+ </td>
+ <td><a href="/packages/?packager=unknown"
+ title="View all packages last updated by unknown">
+ <strong>{{ orphan.updated_count }}</strong> packages</a>
+ </td>
+ </tr>
+ </thead>
+ <tbody>
+ {% for maint in maintainers %}
+ <tr class="{% cycle 'odd' 'even' %}">
+ <td>{{ maint.get_full_name }}</td>
+ <td><a href="/packages/?maintainer={{ maint.username }}"
+ title="View all packages maintained by {{ maint.get_full_name }}">
+ <strong>{{ maint.package_count }}</strong> packages</a>
+ </td>
+ <td><a href="/packages/?maintainer={{ maint.username }}&amp;flagged=Flagged"
+ title="View all flagged packages maintained by {{ maint.get_full_name }}">
+ <strong>{{ maint.flagged_count }}</strong> packages</a>
+ </td>
+ <td><a href="/packages/?packager={{ maint.username }}"
+ title="View all packages last updated by {{ maint.get_full_name }}">
+ <strong>{{ maint.updated_count }}</strong> packages</a>
+ </td>
+ </tr>
+ {% endfor %}
+ </tbody>
+ </table>
+</div>{# #dash-by-developer #}
+{% endcache %}
diff --git a/templates/packages/signoffs.html b/templates/packages/signoffs.html
index a159e998..571cc32d 100644
--- a/templates/packages/signoffs.html
+++ b/templates/packages/signoffs.html
@@ -68,7 +68,7 @@
{% endif %}
<td>{% include "packages/signoff_cell.html" %}</td>
<td class="wrap note">{% if not group.default_spec %}{% with group.specification as spec %}{% comment %}
- {% endcomment %}{% if spec.required != 2 %}Required signoffs: {{ spec.required }}<br/>{% endif %}{% comment %}
+ {% endcomment %}{% if spec.required != spec.arch.required_signoffs %}Required signoffs: {{ spec.required }}<br/>{% endif %}{% comment %}
{% endcomment %}{% if not spec.enabled %}Signoffs are not currently enabled<br/>{% endif %}{% comment %}
{% endcomment %}{% if spec.known_bad %}Package is known to be bad<br/>{% endif %}{% comment %}
{% endcomment %}{{ spec.comments|default:""|linebreaksbr }}