From ac2278423a3d449fdfe8c813f1f2d391ef9aff08 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Thu, 3 Nov 2011 14:59:00 -0500 Subject: Many signoff page improvements Add a new 'SignoffSpecification' model which will capture metadata regarding a specific package if it differs from the norm- e.g. more or less than 2 required signoffs, is known to be bad, a comment from the maintainer, etc. The groundwork is laid here; much of this will still need to be wired up in the future. Enhance the view with a lot more JS prettiness and add revoking of signoffs. The signoff page can be filtered and the links and all the fun stuff are totally dynamic now. Signed-off-by: Dan McGee --- packages/urls.py | 1 + 1 file changed, 1 insertion(+) (limited to 'packages/urls.py') diff --git a/packages/urls.py b/packages/urls.py index d7d01170..576e3279 100644 --- a/packages/urls.py +++ b/packages/urls.py @@ -10,6 +10,7 @@ (r'^unflag/$', 'unflag'), (r'^unflag/all/$', 'unflag_all'), (r'^signoff/$', 'signoff_package'), + (r'^signoff/revoke/$', 'signoff_package', {'revoke': True}), (r'^download/$', 'download'), ) -- cgit v1.2.3-54-g00ecf From 8187b87143081a2be75032db91287f9deb9d1f89 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Thu, 3 Nov 2011 19:10:07 -0500 Subject: Add signoff options form and data entry page This allows the criteria and other information about certain signoffs to be overridden as necessary. Signed-off-by: Dan McGee --- packages/models.py | 52 ++++++++++++++++++++++++++------- packages/urls.py | 1 + packages/views.py | 40 ++++++++++++++++++++++++- templates/packages/signoff_cell.html | 15 ++++++++-- templates/packages/signoff_options.html | 18 ++++++++++++ templates/packages/signoffs.html | 3 +- 6 files changed, 113 insertions(+), 16 deletions(-) create mode 100644 templates/packages/signoff_options.html (limited to 'packages/urls.py') diff --git a/packages/models.py b/packages/models.py index 3c319fe7..a2b53a06 100644 --- a/packages/models.py +++ b/packages/models.py @@ -38,6 +38,22 @@ def __unicode__(self): class Meta: unique_together = (('pkgbase', 'user', 'type'),) + +class SignoffSpecificationManager(models.Manager): + def get_from_package(self, pkg): + '''Utility method to pull all relevant name-version fields from a + package and get a matching specification.''' + return self.get( + pkgbase=pkg.pkgbase, pkgver=pkg.pkgver, pkgrel=pkg.pkgrel, + epoch=pkg.epoch, arch=pkg.arch, repo=pkg.repo) + + def get_or_create_from_package(self, pkg): + '''Utility method to pull all relevant name-version fields from a + package and get or create a matching specification.''' + return self.get_or_create( + pkgbase=pkg.pkgbase, pkgver=pkg.pkgver, pkgrel=pkg.pkgrel, + epoch=pkg.epoch, arch=pkg.arch, repo=pkg.repo) + class SignoffSpecification(models.Model): ''' A specification for the signoff policy for this particular revision of a @@ -53,25 +69,40 @@ class SignoffSpecification(models.Model): repo = models.ForeignKey('main.Repo') user = models.ForeignKey(User) created = models.DateTimeField(editable=False) - required = models.PositiveIntegerField(default=2) - enabled = models.BooleanField(default=True) - known_bad = models.BooleanField(default=False) + required = models.PositiveIntegerField(default=2, + help_text="How many signoffs are required for this package?") + enabled = models.BooleanField(default=True, + help_text="Is this package eligible for signoffs?") + known_bad = models.BooleanField(default=False, + help_text="Is package is known to be broken in some way?") comments = models.TextField(null=True, blank=True) + objects = SignoffSpecificationManager() + + @property + def full_version(self): + if self.epoch > 0: + return u'%d:%s-%s' % (self.epoch, self.pkgver, self.pkgrel) + return u'%s-%s' % (self.pkgver, self.pkgrel) + + def __unicode__(self): + return u'%s-%s' % (self.pkgbase, self.full_version) + + class SignoffManager(models.Manager): def get_from_package(self, pkg, user, revoked=False): '''Utility method to pull all relevant name-version fields from a - package and create a matching signoff.''' + package and get a matching signoff.''' not_revoked = not revoked - return Signoff.objects.get( + return self.get( pkgbase=pkg.pkgbase, pkgver=pkg.pkgver, pkgrel=pkg.pkgrel, epoch=pkg.epoch, arch=pkg.arch, repo=pkg.repo, revoked__isnull=not_revoked, user=user) def get_or_create_from_package(self, pkg, user): '''Utility method to pull all relevant name-version fields from a - package and create a matching signoff.''' - return Signoff.objects.get_or_create( + package and get or create a matching signoff.''' + return self.get_or_create( pkgbase=pkg.pkgbase, pkgver=pkg.pkgver, pkgrel=pkg.pkgrel, epoch=pkg.epoch, arch=pkg.arch, repo=pkg.repo, revoked=None, user=user) @@ -196,9 +227,8 @@ def remove_inactive_maintainers(sender, instance, created, **kwargs): post_save.connect(remove_inactive_maintainers, sender=User, dispatch_uid="packages.models") -pre_save.connect(set_created_field, sender=PackageRelation, - dispatch_uid="packages.models") -pre_save.connect(set_created_field, sender=Signoff, - dispatch_uid="packages.models") +for sender in (PackageRelation, SignoffSpecification, Signoff): + pre_save.connect(set_created_field, sender=sender, + dispatch_uid="packages.models") # vim: set ts=4 sw=4 et: diff --git a/packages/urls.py b/packages/urls.py index 576e3279..4d391a3c 100644 --- a/packages/urls.py +++ b/packages/urls.py @@ -11,6 +11,7 @@ (r'^unflag/all/$', 'unflag_all'), (r'^signoff/$', 'signoff_package'), (r'^signoff/revoke/$', 'signoff_package', {'revoke': True}), + (r'^signoff/options/$', 'signoff_options'), (r'^download/$', 'download'), ) diff --git a/packages/views.py b/packages/views.py index e102760b..66bcd3fc 100644 --- a/packages/views.py +++ b/packages/views.py @@ -25,7 +25,7 @@ from main.models import Package, PackageFile, Arch, Repo from main.utils import make_choice from mirrors.models import MirrorUrl -from .models import PackageRelation, PackageGroup, Signoff +from .models import PackageRelation, PackageGroup, SignoffSpecification, Signoff from .utils import (get_group_info, get_differences_info, get_wrong_permissions, get_signoff_groups, approved_by_signoffs) @@ -417,6 +417,44 @@ def signoff_package(request, name, repo, arch, revoke=False): return redirect('package-signoffs') +class SignoffOptionsForm(forms.ModelForm): + class Meta: + model = SignoffSpecification + fields = ('required', 'enabled', 'known_bad', 'comments') + +@permission_required('main.change_package') +@never_cache +def signoff_options(request, name, repo, arch): + packages = get_list_or_404(Package, pkgbase=name, + arch__name=arch, repo__name__iexact=repo, repo__testing=True) + package = packages[0] + + # TODO ensure submitter is maintainer and/or packager + + try: + spec = SignoffSpecification.objects.get_from_package(package) + except SignoffSpecification.DoesNotExist: + # create a fake one, but don't save it just yet + spec = SignoffSpecification(pkgbase=package.pkgbase, + pkgver=package.pkgver, pkgrel=package.pkgrel, + epoch=package.epoch, arch=package.arch, repo=package.repo) + spec.user = request.user + + if request.POST: + form = SignoffOptionsForm(request.POST, instance=spec) + if form.is_valid(): + form.save() + return redirect('package-signoffs') + else: + form = SignoffOptionsForm(instance=spec) + + context = { + 'packages': packages, + 'package': package, + 'form': form, + } + return direct_to_template(request, 'packages/signoff_options.html', context) + def flaghelp(request): return direct_to_template(request, 'packages/flaghelp.html') diff --git a/templates/packages/signoff_cell.html b/templates/packages/signoff_cell.html index 87216193..0a630119 100644 --- a/templates/packages/signoff_cell.html +++ b/templates/packages/signoff_cell.html @@ -1,12 +1,23 @@ +{% spaceless %} +{% if group.signoffs %}
    {% for signoff in group.signoffs %}
  • {{ signoff.user }}{% if signoff.revoked %} (revoked){% endif %}
  • {% endfor %}
+{% endif %} {% if group.user_signed_off %} -
+ Revoke Signoff
{% else %} -
+ Signoff
{% endif %} +{% if group.packager == user %} +
+ Packager Options +
+{% endif %} +{% endspaceless %} diff --git a/templates/packages/signoff_options.html b/templates/packages/signoff_options.html new file mode 100644 index 00000000..ee9b8b47 --- /dev/null +++ b/templates/packages/signoff_options.html @@ -0,0 +1,18 @@ +{% extends "base.html" %} + +{% block title %}Arch Linux - Package Signoff Options - {{ package.pkgbase }} {{ package.full_version }} ({{ package.arch.name }}){% endblock %} +{% block head %}{% endblock %} +{% block navbarclass %}anb-packages{% endblock %} + +{% block content %} +
+

Package Signoff Options: {{ package.pkgbase }} {{ package.full_version }} ({{ package.arch.name }})

+
{% csrf_token %} +
+ {{ form.as_p }} +
+

+
+ +
+{% endblock %} diff --git a/templates/packages/signoffs.html b/templates/packages/signoffs.html index 0bdc6d46..9bc7fd74 100644 --- a/templates/packages/signoffs.html +++ b/templates/packages/signoffs.html @@ -50,8 +50,7 @@

Filter Displayed Signoffs

{{ group.packager|default:"Unknown" }} {{ group.packages|length }} {{ group.last_update|date }} - - {{ group.approved|yesno|capfirst }} + {{ group.approved|yesno|capfirst }} {% include "packages/signoff_cell.html" %} {% endfor %} -- cgit v1.2.3-54-g00ecf From d80f4236d01f70380f71a46dd98f1f789d91d31c Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Thu, 10 Nov 2011 18:09:19 -0600 Subject: Add package signoffs JSON view This allows access to the same data (and even a bit more) from the signoffs overview page in a machine-friendly way. Signed-off-by: Dan McGee --- mirrors/utils.py | 3 ++- packages/urls.py | 1 + packages/views.py | 47 ++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 49 insertions(+), 2 deletions(-) (limited to 'packages/urls.py') diff --git a/mirrors/utils.py b/mirrors/utils.py index 686ec581..8518b3ba 100644 --- a/mirrors/utils.py +++ b/mirrors/utils.py @@ -40,7 +40,8 @@ def get_mirror_statuses(cutoff=default_cutoff): last_sync=Max('logs__last_sync'), last_check=Max('logs__check_time'), duration_avg=Avg('logs__duration'), - duration_stddev=StdDev('logs__duration') + #duration_stddev=StdDev('logs__duration') + duration_stddev=Max('logs__duration') ).order_by('-last_sync', '-duration_avg') # The Django ORM makes it really hard to get actual average delay in the diff --git a/packages/urls.py b/packages/urls.py index 4d391a3c..1f25e3fd 100644 --- a/packages/urls.py +++ b/packages/urls.py @@ -18,6 +18,7 @@ urlpatterns = patterns('packages.views', (r'^flaghelp/$', 'flaghelp'), (r'^signoffs/$', 'signoffs', {}, 'package-signoffs'), + (r'^signoffs/json/$', 'signoffs_json', {}, 'package-signoffs-json'), (r'^update/$', 'update'), (r'^$', 'search', {}, 'packages-search'), diff --git a/packages/views.py b/packages/views.py index 3c0c2bee..cac5d076 100644 --- a/packages/views.py +++ b/packages/views.py @@ -29,7 +29,8 @@ from mirrors.models import MirrorUrl from .models import PackageRelation, PackageGroup, SignoffSpecification, Signoff from .utils import (get_group_info, get_differences_info, - get_wrong_permissions, get_signoff_groups, approved_by_signoffs) + get_wrong_permissions, get_signoff_groups, approved_by_signoffs, + PackageSignoffGroup) class PackageJSONEncoder(DjangoJSONEncoder): pkg_attributes = [ 'pkgname', 'pkgbase', 'repo', 'arch', 'pkgver', @@ -371,6 +372,7 @@ def unflag_all(request, name, repo, arch): pkgs.update(flag_date=None) return redirect(pkg) + @permission_required('main.change_package') @never_cache def signoffs(request): @@ -496,6 +498,49 @@ def signoff_options(request, name, repo, arch): } return direct_to_template(request, 'packages/signoff_options.html', context) +class SignoffJSONEncoder(DjangoJSONEncoder): + '''Base JSONEncoder extended to handle all serialization of all classes + related to signoffs.''' + signoff_group_attrs = ['arch', 'last_update', 'maintainers', 'packager', + 'pkgbase', 'repo', 'signoffs', 'target_repo', 'version'] + signoff_spec_attrs = ['required', 'enabled', 'known_bad', 'comments'] + signoff_attrs = ['user', 'created', 'revoked'] + + def default(self, obj): + if isinstance(obj, PackageSignoffGroup): + data = dict((attr, getattr(obj, attr)) + for attr in self.signoff_group_attrs) + data['package_count'] = len(obj.packages) + data['approved'] = obj.approved() + data.update((attr, getattr(obj.specification, attr)) + for attr in self.signoff_spec_attrs) + return data + elif isinstance(obj, Signoff): + data = dict((attr, getattr(obj, attr)) + for attr in self.signoff_attrs) + return data + elif isinstance(obj, Arch) or isinstance(obj, Repo): + return unicode(obj) + elif isinstance(obj, User): + return obj.username + elif isinstance(obj, set): + return list(obj) + return super(SignoffJSONEncoder, self).default(obj) + +@permission_required('main.change_package') +@never_cache +def signoffs_json(request): + signoff_groups = sorted(get_signoff_groups(), key=attrgetter('pkgbase')) + data = { + 'version': 1, + 'signoff_groups': signoff_groups, + } + to_json = simplejson.dumps(data, ensure_ascii=False, + cls=SignoffJSONEncoder) + response = HttpResponse(to_json, mimetype='application/json') + return response + + def flaghelp(request): return direct_to_template(request, 'packages/flaghelp.html') -- cgit v1.2.3-54-g00ecf