diff options
-rw-r--r-- | devel/urls.py | 1 | ||||
-rw-r--r-- | devel/views.py | 21 | ||||
-rw-r--r-- | main/admin.py | 2 | ||||
-rw-r--r-- | main/fixtures/arches.json | 81 | ||||
-rw-r--r-- | main/migrations/0064_auto__add_field_arch_required_signoffs.py | 112 | ||||
-rw-r--r-- | main/models.py | 2 | ||||
-rw-r--r-- | packages/management/commands/populate_signoffs.py | 6 | ||||
-rw-r--r-- | packages/models.py | 12 | ||||
-rw-r--r-- | packages/utils.py | 7 | ||||
-rw-r--r-- | settings.py | 6 | ||||
-rw-r--r-- | sitestatic/archweb.js | 16 | ||||
-rw-r--r-- | templates/devel/index.html | 148 | ||||
-rw-r--r-- | templates/devel/packages.html | 2 | ||||
-rw-r--r-- | templates/devel/stats.html | 120 | ||||
-rw-r--r-- | templates/packages/signoffs.html | 2 |
15 files changed, 353 insertions, 185 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 f7e7fdc0..6ebd0c15 100644 --- a/main/fixtures/arches.json +++ b/main/fixtures/arches.json @@ -1,42 +1,45 @@ [ - { - "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": 4, - "model": "main.arch", - "fields": { - "agnostic": false, - "name": "mips64el" - } - }, - { - "pk": 5, - "model": "main.arch", - "fields": { - "agnostic": false, - "name": "i586" - } +{ + "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 + } +}, +{ + "pk": 4, + "model": "main.arch", + "fields": { + "agnostic": false, + "name": "mips64el" + } +}, +{ + "pk": 5, + "model": "main.arch", + "fields": { + "agnostic": false, + "name": "i586" + } +} ] 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 e77dbace..fade0855 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+):)?(.+)-([^-]+)$') @@ -301,7 +301,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] @@ -312,6 +311,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): @@ -414,7 +414,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/settings.py b/settings.py index a6be4610..1fc0a496 100644 --- a/settings.py +++ b/settings.py @@ -177,6 +177,12 @@ BUGTRACKER_RELENG_URL = 'https://labs.parabola.nu/projects/isos' # URL for linking to projects in git PROJECTS_URL = 'https://projects.parabolagnulinux.org' +# URL for linking to the release engineering/iso project on the bugtracker +BUGTRACKER_RELENG_URL = 'https://bugs.archlinux.org/index.php?project=6' + +# URL for linking to projects in git +PROJECTS_URL = 'https://projects.archlinux.org' + # Trackers used for ISO download magnet links TORRENT_TRACKERS = ( 'udp://tracker.publicbt.com:80', 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 036f81fe..919314c8 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 > 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 }}&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 }}&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&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 }}&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 }}&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 }}&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&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 }}&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 }} |