From a2317295bb4b8c52a83c9a70263fcc9cc73621f4 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Thu, 8 Dec 2011 13:52:53 -0600 Subject: Remove auto-deletion of package relations on inactive users We have a page where these can all be managed now, so best leave it alone in case someone accidentally marks a user inactive and all the data is lost. Signed-off-by: Dan McGee --- packages/models.py | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/packages/models.py b/packages/models.py index 0d02ab31..387db5c2 100644 --- a/packages/models.py +++ b/packages/models.py @@ -229,16 +229,7 @@ class Replacement(models.Model): ordering = ['name'] -def remove_inactive_maintainers(sender, instance, created, **kwargs): - # instance is an auth.models.User; we want to remove any existing - # maintainer relations if the user is no longer active - if not instance.is_active: - maint_relations = PackageRelation.objects.filter(user=instance, - type=PackageRelation.MAINTAINER) - maint_relations.delete() - -post_save.connect(remove_inactive_maintainers, sender=User, - dispatch_uid="packages.models") +# hook up some signals for sender in (PackageRelation, SignoffSpecification, Signoff): pre_save.connect(set_created_field, sender=sender, dispatch_uid="packages.models") -- cgit v1.2.3-2-g168b From 3e094a548f409e4a87454764f6baf814464d9619 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Thu, 8 Dec 2011 14:39:30 -0600 Subject: Add a new FlagRequest model This will be used to store all of the submitted data we get via flag out of date forms on the website. Signed-off-by: Dan McGee --- main/models.py | 2 +- packages/admin.py | 11 +- packages/migrations/0012_auto__add_flagrequest.py | 201 ++++++++++++++++++++++ packages/models.py | 34 +++- 4 files changed, 241 insertions(+), 7 deletions(-) create mode 100644 packages/migrations/0012_auto__add_flagrequest.py diff --git a/main/models.py b/main/models.py index 9156fb51..cefebf76 100644 --- a/main/models.py +++ b/main/models.py @@ -6,7 +6,6 @@ from django.forms import ValidationError from .fields import PositiveBigIntegerField, PGPKeyField from .utils import cache_function, make_choice, set_created_field -from packages.models import PackageRelation from datetime import datetime from itertools import groupby @@ -193,6 +192,7 @@ class Package(models.Model): @property def maintainers(self): + from packages.models import PackageRelation if self._maintainers is None: self._maintainers = User.objects.filter( package_relations__pkgbase=self.pkgbase, diff --git a/packages/admin.py b/packages/admin.py index 01b6ed6c..14fa8960 100644 --- a/packages/admin.py +++ b/packages/admin.py @@ -1,12 +1,21 @@ from django.contrib import admin -from .models import PackageRelation +from .models import PackageRelation, FlagRequest class PackageRelationAdmin(admin.ModelAdmin): list_display = ('user', 'pkgbase', 'type', 'created') list_filter = ('type', 'user') search_fields = ('user__username', 'pkgbase') + date_hierarchy = 'created' + +class FlagRequestAdmin(admin.ModelAdmin): + list_display = ('pkgbase', 'created', 'who', 'is_spam', 'is_legitimate', + 'message') + list_filter = ('is_spam', 'is_legitimate') + search_fields = ('pkgbase', 'user_email', 'message') + date_hierarchy = 'created' admin.site.register(PackageRelation, PackageRelationAdmin) +admin.site.register(FlagRequest, FlagRequestAdmin) # vim: set ts=4 sw=4 et: diff --git a/packages/migrations/0012_auto__add_flagrequest.py b/packages/migrations/0012_auto__add_flagrequest.py new file mode 100644 index 00000000..a501daff --- /dev/null +++ b/packages/migrations/0012_auto__add_flagrequest.py @@ -0,0 +1,201 @@ +# encoding: utf-8 +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding model 'FlagRequest' + db.create_table('packages_flagrequest', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], null=True, blank=True)), + ('user_email', self.gf('django.db.models.fields.EmailField')(max_length=75)), + ('created', self.gf('django.db.models.fields.DateTimeField')()), + ('ip_address', self.gf('django.db.models.fields.IPAddressField')(max_length=15)), + ('pkgbase', self.gf('django.db.models.fields.CharField')(max_length=255, db_index=True)), + ('repo', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['main.Repo'])), + ('num_packages', self.gf('django.db.models.fields.PositiveIntegerField')(default=1)), + ('message', self.gf('django.db.models.fields.TextField')(blank=True)), + ('is_spam', self.gf('django.db.models.fields.BooleanField')(default=False)), + ('is_legitimate', self.gf('django.db.models.fields.BooleanField')(default=True)), + )) + db.send_create_signal('packages', ['FlagRequest']) + + if db.backend_name == 'mysql': + # stupid f#$%ing storage of IP address as a 15 character type + db.execute("ALTER TABLE packages_flagrequest " + "MODIFY ip_address char(39) NOT NULL") + + + def backwards(self, orm): + # Deleting model 'FlagRequest' + db.delete_table('packages_flagrequest') + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + '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': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + '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': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + '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': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + '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'}), + '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'}) + }, + 'main.arch': { + 'Meta': {'ordering': "['name']", 'object_name': 'Arch', 'db_table': "'arches'"}, + 'agnostic': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}) + }, + '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'", 'to': "orm['main.Arch']"}), + 'build_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), + 'compressed_size': ('main.fields.PositiveBigIntegerField', [], {}), + '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'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'installed_size': ('main.fields.PositiveBigIntegerField', [], {}), + 'last_update': ('django.db.models.fields.DateTimeField', [], {}), + 'packager': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': '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'", 'to': "orm['main.Repo']"}), + 'url': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}) + }, + 'main.repo': { + 'Meta': {'ordering': "['name']", 'object_name': 'Repo', 'db_table': "'repos'"}, + 'bugs_category': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), + 'bugs_project': ('django.db.models.fields.SmallIntegerField', [], {'default': '1'}), + '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'}) + }, + 'packages.conflict': { + 'Meta': {'ordering': "['name']", 'object_name': 'Conflict'}, + 'comparison': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'pkg': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'conflicts'", 'to': "orm['main.Package']"}), + 'version': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'}) + }, + 'packages.flagrequest': { + 'Meta': {'object_name': 'FlagRequest'}, + 'created': ('django.db.models.fields.DateTimeField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'ip_address': ('django.db.models.fields.IPAddressField', [], {'max_length': '15'}), + 'is_legitimate': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_spam': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'message': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'num_packages': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}), + 'pkgbase': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'repo': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Repo']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}), + 'user_email': ('django.db.models.fields.EmailField', [], {'max_length': '75'}) + }, + 'packages.license': { + 'Meta': {'ordering': "['name']", 'object_name': 'License'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'pkg': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'licenses'", 'to': "orm['main.Package']"}) + }, + 'packages.packagegroup': { + 'Meta': {'object_name': 'PackageGroup'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'pkg': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'groups'", 'to': "orm['main.Package']"}) + }, + 'packages.packagerelation': { + 'Meta': {'unique_together': "(('pkgbase', 'user', 'type'),)", 'object_name': 'PackageRelation'}, + 'created': ('django.db.models.fields.DateTimeField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'pkgbase': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'type': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'package_relations'", 'to': "orm['auth.User']"}) + }, + 'packages.provision': { + 'Meta': {'ordering': "['name']", 'object_name': 'Provision'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'pkg': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'provides'", 'to': "orm['main.Package']"}), + 'version': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'}) + }, + 'packages.replacement': { + 'Meta': {'ordering': "['name']", 'object_name': 'Replacement'}, + 'comparison': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'pkg': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'replaces'", 'to': "orm['main.Package']"}), + 'version': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'}) + }, + 'packages.signoff': { + 'Meta': {'object_name': 'Signoff'}, + 'arch': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Arch']"}), + 'comments': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'created': ('django.db.models.fields.DateTimeField', [], {}), + 'epoch': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'pkgbase': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + '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', [], {'to': "orm['main.Repo']"}), + 'revoked': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'package_signoffs'", 'to': "orm['auth.User']"}) + }, + 'packages.signoffspecification': { + 'Meta': {'object_name': 'SignoffSpecification'}, + 'arch': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Arch']"}), + 'comments': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'created': ('django.db.models.fields.DateTimeField', [], {}), + 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'epoch': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'known_bad': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'pkgbase': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + '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', [], {'to': "orm['main.Repo']"}), + 'required': ('django.db.models.fields.PositiveIntegerField', [], {'default': '2'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True'}) + } + } + + complete_apps = ['packages'] diff --git a/packages/models.py b/packages/models.py index 387db5c2..77cade68 100644 --- a/packages/models.py +++ b/packages/models.py @@ -4,6 +4,7 @@ from django.db import models from django.db.models.signals import pre_save, post_save from django.contrib.auth.models import User +from main.models import Arch, Repo from main.utils import set_created_field class PackageRelation(models.Model): @@ -71,8 +72,8 @@ class SignoffSpecification(models.Model): pkgver = models.CharField(max_length=255) pkgrel = models.CharField(max_length=255) epoch = models.PositiveIntegerField(default=0) - arch = models.ForeignKey('main.Arch') - repo = models.ForeignKey('main.Repo') + arch = models.ForeignKey(Arch) + repo = models.ForeignKey(Repo) user = models.ForeignKey(User, null=True) created = models.DateTimeField(editable=False) required = models.PositiveIntegerField(default=2, @@ -134,8 +135,8 @@ class Signoff(models.Model): pkgver = models.CharField(max_length=255) pkgrel = models.CharField(max_length=255) epoch = models.PositiveIntegerField(default=0) - arch = models.ForeignKey('main.Arch') - repo = models.ForeignKey('main.Repo') + arch = models.ForeignKey(Arch) + repo = models.ForeignKey(Repo) user = models.ForeignKey(User, related_name="package_signoffs") created = models.DateTimeField(editable=False) revoked = models.DateTimeField(null=True) @@ -164,6 +165,29 @@ class Signoff(models.Model): return u'%s-%s: %s%s' % ( self.pkgbase, self.full_version, self.user, revoked) + +class FlagRequest(models.Model): + user = models.ForeignKey(User, blank=True, null=True) + user_email = models.EmailField('email address') + created = models.DateTimeField(editable=False) + ip_address = models.IPAddressField('IP address') + pkgbase = models.CharField(max_length=255, db_index=True) + repo = models.ForeignKey(Repo) + num_packages = models.PositiveIntegerField('number of packages', default=1) + message = models.TextField('message to developer', blank=True) + is_spam = models.BooleanField(default=False, + help_text="Is this comment from a real person?") + is_legitimate = models.BooleanField(default=True, + help_text="Is this actually an out-of-date flag request?") + + def who(self): + if self.user: + return self.user.get_full_name() + return self.user_email + + def __unicode__(self): + return u'%s from %s on %s' % (self.pkgbase, self.who(), self.created) + class PackageGroup(models.Model): ''' Represents a group a package is in. There is no actual group entity, @@ -230,7 +254,7 @@ class Replacement(models.Model): # hook up some signals -for sender in (PackageRelation, SignoffSpecification, Signoff): +for sender in (PackageRelation, SignoffSpecification, Signoff, FlagRequest): pre_save.connect(set_created_field, sender=sender, dispatch_uid="packages.models") -- cgit v1.2.3-2-g168b From 5006da56476ebd3614cab68c574ab893e82d5aaf Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Thu, 8 Dec 2011 15:02:28 -0600 Subject: Store flag requests in the database This makes them persistent rather than the transient beings they currently are. We attempt to capture all the metadata we need to be able to do things with this later- aka IP address (for spam checking later), fields that allow us to mark the request as spam or not an actual out-of-date report, etc. As a bonus, logged-in developers now get the email address field filled in for free. Yay. Signed-off-by: Dan McGee --- packages/views/flag.py | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/packages/views/flag.py b/packages/views/flag.py index 7e9d87c7..4132f9a7 100644 --- a/packages/views/flag.py +++ b/packages/views/flag.py @@ -9,6 +9,7 @@ from django.template import loader, Context from django.views.generic.simple import direct_to_template from django.views.decorators.cache import never_cache +from ..models import FlagRequest from main.models import Package @@ -17,7 +18,7 @@ def flaghelp(request): class FlagForm(forms.Form): email = forms.EmailField(label='* E-mail Address') - usermessage = forms.CharField(label='Message To Dev', + message = forms.CharField(label='Message To Dev', widget=forms.Textarea, required=False) # The field below is used to filter out bots that blindly fill out all # input elements @@ -47,6 +48,16 @@ def flag(request, name, repo, arch): flagged_pkgs = list(pkgs) pkgs.update(flag_date=datetime.utcnow()) + # store our flag request + flag_request = FlagRequest(user_email=form.cleaned_data['email'], + ip_address=request.META.get('REMOTE_ADDR', '127.0.0.1'), + pkgbase=pkg.pkgbase, repo=pkg.repo, + num_packages=len(flagged_pkgs), + message=form.cleaned_data['message']) + if request.user.is_authenticated(): + flag_request.user = request.user + flag_request.save() + maints = pkg.maintainers if not maints: toemail = settings.NOTIFICATIONS @@ -65,7 +76,7 @@ def flag(request, name, repo, arch): tmpl = loader.get_template('packages/outofdate.txt') ctx = Context({ 'email': form.cleaned_data['email'], - 'message': form.cleaned_data['usermessage'], + 'message': form.cleaned_data['message'], 'pkg': pkg, 'packages': flagged_pkgs, }) @@ -78,7 +89,10 @@ def flag(request, name, repo, arch): return redirect('package-flag-confirmed', name=name, repo=repo, arch=arch) else: - form = FlagForm() + initial = {} + if request.user.is_authenticated(): + initial['email'] = request.user.email + form = FlagForm(initial=initial) context = { 'package': pkg, -- cgit v1.2.3-2-g168b From 4fa709ea86c8eefac05a848187fc7281edff8fa9 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Fri, 9 Dec 2011 09:28:49 -0600 Subject: populate_signoffs: add an SVN log cache for duplicate lookups Signed-off-by: Dan McGee --- packages/management/commands/populate_signoffs.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/packages/management/commands/populate_signoffs.py b/packages/management/commands/populate_signoffs.py index ce5ec734..42496e9d 100644 --- a/packages/management/commands/populate_signoffs.py +++ b/packages/management/commands/populate_signoffs.py @@ -44,6 +44,9 @@ class Command(NoArgsCommand): return add_signoff_comments() def svn_log(pkgbase, repo): + '''Retrieve the most recent SVN log entry for the given pkgbase and + repository. The configured setting SVN_BASE_URL is used along with the + svn_root for each repository to form the correct URL.''' path = '%s%s/%s/trunk/' % (settings.SVN_BASE_URL, repo.svn_root, pkgbase) cmd = ['svn', 'log', '--limit=1', '--xml', path] log_data = subprocess.check_output(cmd) @@ -59,6 +62,17 @@ def svn_log(pkgbase, repo): 'message': xml.findtext('logentry/msg'), } +def cached_svn_log(pkgbase, repo): + '''Retrieve the cached version of the SVN log if possible, else delegate to + svn_log() to do the work and cache the result.''' + key = (pkgbase, repo) + if key in cached_svn_log.cache: + return cached_svn_log.cache[key] + log = svn_log(pkgbase, repo) + cached_svn_log.cache[key] = log + return log +cached_svn_log.cache = {} + def create_specification(package, log, finder): trimmed_message = log['message'].strip() spec = SignoffSpecification(pkgbase=package.pkgbase, @@ -80,7 +94,7 @@ def add_signoff_comments(): continue logger.debug("getting SVN log for %s (%s)", group.pkgbase, group.repo) - log = svn_log(group.pkgbase, group.repo) + log = cached_svn_log(group.pkgbase, group.repo) logger.info("creating spec with SVN message for %s", group.pkgbase) spec = create_specification(group.packages[0], log, finder) spec.save() -- cgit v1.2.3-2-g168b From 73cd4adf9ff2b38124501fba1a7d9800e4c1f0d0 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Fri, 9 Dec 2011 09:29:15 -0600 Subject: Add CSS classes to front page package update objects This adds the repo name, 'staging', and 'testing' as appropriate to a classes field on the package updates object. This means we don't have to update the CSS stylesheet to include hardcoded names of repositories (e.g., 'kde-unstable'). Signed-off-by: Dan McGee --- public/utils.py | 7 +++++++ sitestatic/archweb.css | 8 ++------ templates/public/index.html | 2 +- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/public/utils.py b/public/utils.py index 30c76ac1..6566b8c4 100644 --- a/public/utils.py +++ b/public/utils.py @@ -11,6 +11,13 @@ class RecentUpdate(object): self.pkgbase = first.pkgbase self.repo = first.repo self.version = '' + self.classes = set() + + self.classes.add(self.repo.name.lower()) + if self.repo.testing: + self.classes.add('testing') + if self.repo.staging: + self.classes.add('staging') packages = sorted(packages, key=attrgetter('arch', 'pkgname')) # split the packages into two lists. we need to prefer packages diff --git a/sitestatic/archweb.css b/sitestatic/archweb.css index 3407cb4b..46fd7844 100644 --- a/sitestatic/archweb.css +++ b/sitestatic/archweb.css @@ -481,15 +481,11 @@ h3 span.arrow { text-align: right; } - #pkg-updates span.testing, - #pkg-updates span.community-testing, - span.multilib-testing { + #pkg-updates span.testing { font-style: italic; } - #pkg-updates span.staging, - #pkg-updates span.community-staging, - span.multilib-staging { + #pkg-updates span.staging { font-style: italic; color: #ff8040; } diff --git a/templates/public/index.html b/templates/public/index.html index 4bd26f6b..24e61558 100644 --- a/templates/public/index.html +++ b/templates/public/index.html @@ -104,7 +104,7 @@ {% for update in pkg_updates %} - +
{{ update.pkgbase }} {{ update.version }}{{ update.pkgbase }} {{ update.version }} {% for pkg in update.package_links %}{{ pkg.arch }}{% if not forloop.last %}/{% endif %}{% endfor %} -- cgit v1.2.3-2-g168b From d198137d718a5dfcaacedc3e2e3adf86c3192cd7 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Fri, 9 Dec 2011 09:30:18 -0600 Subject: Prefilter packages when looking up uncompressed man/info pages This vastly speeds up the reports if you just want to look at your own packages and not the complete list, especially if the list of packages you maintain is relatively short. Signed-off-by: Dan McGee --- devel/views.py | 42 +++++++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/devel/views.py b/devel/views.py index 59e2b6cb..79272c98 100644 --- a/devel/views.py +++ b/devel/views.py @@ -6,7 +6,7 @@ from django.contrib.auth.models import User, Group from django.contrib.sites.models import Site from django.core.mail import send_mail from django.db import transaction -from django.db.models import F, Q +from django.db.models import F from django.http import Http404 from django.shortcuts import get_object_or_404 from django.template import loader, Context @@ -150,6 +150,15 @@ def report(request, report, username=None): packages = Package.objects.normal() names = attrs = user = None + if username: + user = get_object_or_404(User, username=username, is_active=True) + maintained = PackageRelation.objects.filter(user=user, + type=PackageRelation.MAINTAINER).values('pkgbase') + packages = packages.filter(pkgbase__in=maintained) + + maints = User.objects.filter(id__in=PackageRelation.objects.filter( + type=PackageRelation.MAINTAINER).values('user')) + if report == 'old': title = 'Packages last built more than two years ago' cutoff = datetime.utcnow() - timedelta(days=365 * 2) @@ -192,20 +201,24 @@ def report(request, report, username=None): package.compress_type = package.filename.split('.')[-1] elif report == 'uncompressed-man': title = 'Packages with uncompressed manpages' - # magic going on here! Checking for all '.1'...'.9' extensions - invalid_endings = [Q(filename__endswith='.%d' % n) for n in range(1,10)] - invalid_endings.append(Q(filename__endswith='.n')) - bad_files = PackageFile.objects.filter(Q(directory__contains='man') & ( - reduce(operator.or_, invalid_endings)) - ).values_list('pkg_id', flat=True).distinct() + # checking for all '.0'...'.9' + '.n' extensions + bad_files = PackageFile.objects.filter(directory__contains='/man/', + filename__regex=r'\.[0-9n]').exclude(filename__endswith='.gz') + if username: + pkg_ids = set(packages.values_list('id', flat=True)) + bad_files = bad_files.filter(pkg__in=pkg_ids) + bad_files = bad_files.values_list('pkg_id', flat=True).distinct() packages = packages.filter(id__in=set(bad_files)) elif report == 'uncompressed-info': title = 'Packages with uncompressed infopages' - # we don't worry abut looking for '*.info-1', etc., given that an + # we don't worry about looking for '*.info-1', etc., given that an # uncompressed root page probably exists in the package anyway bad_files = PackageFile.objects.filter(directory__endswith='/info/', - filename__endswith='.info').values_list( - 'pkg_id', flat=True).distinct() + filename__endswith='.info') + if username: + pkg_ids = set(packages.values_list('id', flat=True)) + bad_files = bad_files.filter(pkg__in=pkg_ids) + bad_files = bad_files.values_list('pkg_id', flat=True).distinct() packages = packages.filter(id__in=set(bad_files)) elif report == 'unneeded-orphans': title = 'Orphan packages required by no other packages' @@ -217,15 +230,6 @@ def report(request, report, username=None): else: raise Http404 - if username: - user = get_object_or_404(User, username=username, is_active=True) - maintained = PackageRelation.objects.filter(user=user, - type=PackageRelation.MAINTAINER).values('pkgbase') - packages = packages.filter(pkgbase__in=maintained) - - maints = User.objects.filter(id__in=PackageRelation.objects.filter( - type=PackageRelation.MAINTAINER).values('user')) - context = { 'all_maintainers': maints, 'title': title, -- cgit v1.2.3-2-g168b