From dcbee21c1ba281b061db302056c771936925c824 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Tue, 21 Jun 2011 08:56:50 -0500 Subject: Nicer formatting of PGP key display value Signed-off-by: Dan McGee --- main/templatetags/pgp.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) (limited to 'main') diff --git a/main/templatetags/pgp.py b/main/templatetags/pgp.py index d9126dbd..956de892 100644 --- a/main/templatetags/pgp.py +++ b/main/templatetags/pgp.py @@ -3,6 +3,17 @@ from django.conf import settings register = template.Library() +def format_key(key_id): + print len(key_id) + if len(key_id) in (8, 20): + return u'0x%s' % key_id + elif len(key_id) == 40: + # normal display format is 5 groups of 4 hex chars seperated by spaces, + # double space, then 5 more groups of 4 hex chars + split = tuple(key_id[i:i+4] for i in range(0, 40, 4)) + return u'%s  %s' % (' '.join(split[0:5]), ' '.join(split[5:10])) + return u'0x%s' % key_id + @register.simple_tag def pgp_key_link(key_id): if not key_id: @@ -10,10 +21,10 @@ def pgp_key_link(key_id): # Something like 'pgp.mit.edu:11371' pgp_server = getattr(settings, 'PGP_SERVER', None) if not pgp_server: - return "0x%s" % key_id + return format_key(key_id) url = 'http://%s/pks/lookup?op=vindex&fingerprint=on&exact=on&search=0x%s' % \ (pgp_server, key_id) - values = (url, key_id, key_id) - return '0x%s' % values + values = (url, key_id, format_key(key_id)) + return '%s' % values # vim: set ts=4 sw=4 et: -- cgit v1.2.3-2-g168b From 388973d6fd082148c85db0df44e0ee5ed3b6a54b Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Thu, 23 Jun 2011 18:48:39 -0500 Subject: Revert "Remove unused flagged() manager method" Far from unneeded, this is used on the developer dashboard. Silly me. Document this fact as well in the code so we don't screw it up again. This reverts commit 2a44855556531a27e949884d4084c6e5d37082e1. --- main/models.py | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'main') diff --git a/main/models.py b/main/models.py index a5d6c42e..05e63fed 100644 --- a/main/models.py +++ b/main/models.py @@ -73,6 +73,10 @@ class TodolistManager(models.Manager): return self.filter(todolistpkg__complete=False).distinct() class PackageManager(models.Manager): + def flagged(self): + """Used by dev dashboard.""" + return self.filter(flag_date__isnull=False) + def normal(self): return self.select_related('arch', 'repo') -- cgit v1.2.3-2-g168b From 55f6ad0c95323a5bfeca2c322918d21f413d1075 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Thu, 23 Jun 2011 20:19:49 -0500 Subject: Set up queries for staging repos This treats repo.staging special in much the way we already have to treat repo.testing as special. Signed-off-by: Dan McGee --- main/models.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) (limited to 'main') diff --git a/main/models.py b/main/models.py index 05e63fed..e0017069 100644 --- a/main/models.py +++ b/main/models.py @@ -226,6 +226,7 @@ class Package(models.Model): groupby(requiredby, lambda x: x.pkg.id)] # find another package by this name in the opposite testing setup + # TODO: figure out staging exclusions too if not Package.objects.filter(pkgname=self.pkgname, arch=self.arch).exclude(id=self.id).exclude( repo__testing=self.repo.testing).exists(): @@ -241,7 +242,8 @@ class Package(models.Model): dep = dep_pkgs[0] if len(dep_pkgs) > 1: dep_pkgs = [d for d in dep_pkgs - if d.pkg.repo.testing == self.repo.testing] + if d.pkg.repo.testing == self.repo.testing and + d.pkg.repo.staging = self.repo.staging] if len(dep_pkgs) > 0: dep = dep_pkgs[0] trimmed.append(dep) @@ -273,7 +275,8 @@ class Package(models.Model): # grab the first though in case we fail pkg = pkgs[0] # prevents yet more DB queries, these lists should be short - pkgs = [p for p in pkgs if p.repo.testing == self.repo.testing] + pkgs = [p for p in pkgs if p.repo.testing == self.repo.testing + and p.repo.staging = self.repo.staging] if len(pkgs) > 0: pkg = pkgs[0] deps.append({'dep': dep, 'pkg': pkg}) @@ -293,7 +296,8 @@ class Package(models.Model): # this package might be split across repos? just find one # that matches the correct [testing] repo flag pkglist = Package.objects.filter(arch=self.arch, - repo__testing=self.repo.testing, pkgname=self.pkgbase) + repo__testing=self.repo.testing, + repo__staging=self.repo.staging, pkgname=self.pkgbase) if len(pkglist) > 0: return pkglist[0] return None @@ -303,11 +307,12 @@ class Package(models.Model): Return all packages that were built with this one (e.g. share a pkgbase value). The package this method is called on will never be in the list, and we will never return a package that does not have the same - repo.testing flag. For any non-split packages, the return value will be - an empty list. + repo.testing and repo.staging flags. For any non-split packages, the + return value will be an empty list. """ return Package.objects.filter(arch__in=self.applicable_arches(), - repo__testing=self.repo.testing, pkgbase=self.pkgbase).exclude(id=self.id) + repo__testing=self.repo.testing, repo__staging=self.repo.staging, + pkgbase=self.pkgbase).exclude(id=self.id) def is_same_version(self, other): 'is this package similar, name and version-wise, to another' -- cgit v1.2.3-2-g168b From 5bf28d1cabc54dfa68ff64804a31ead419780fdf Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Thu, 23 Jun 2011 20:24:15 -0500 Subject: Add Package.in_staging method Signed-off-by: Dan McGee --- main/models.py | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'main') diff --git a/main/models.py b/main/models.py index e0017069..b52c45fb 100644 --- a/main/models.py +++ b/main/models.py @@ -332,6 +332,17 @@ class Package(models.Model): except Package.DoesNotExist: return None + def in_staging(self): + '''attempt to locate this package in a staging repo; if we are in + a staging repo we will always return None.''' + if self.repo.staging: + return None + try: + return Package.objects.normal().get(repo__staging=True, + pkgname=self.pkgname, arch=self.arch) + except Package.DoesNotExist: + return None + def elsewhere(self): '''attempt to locate this package anywhere else, regardless of architecture or repository. Excludes this package from the list.''' -- cgit v1.2.3-2-g168b From c584ffc9b6bc9bdab1616b096ccaa1c1cd762f69 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Thu, 23 Jun 2011 20:30:31 -0500 Subject: Fix syntax errors, == not = Signed-off-by: Dan McGee --- main/models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'main') diff --git a/main/models.py b/main/models.py index b52c45fb..9010817b 100644 --- a/main/models.py +++ b/main/models.py @@ -243,7 +243,7 @@ class Package(models.Model): if len(dep_pkgs) > 1: dep_pkgs = [d for d in dep_pkgs if d.pkg.repo.testing == self.repo.testing and - d.pkg.repo.staging = self.repo.staging] + d.pkg.repo.staging == self.repo.staging] if len(dep_pkgs) > 0: dep = dep_pkgs[0] trimmed.append(dep) @@ -276,7 +276,7 @@ class Package(models.Model): pkg = pkgs[0] # prevents yet more DB queries, these lists should be short pkgs = [p for p in pkgs if p.repo.testing == self.repo.testing - and p.repo.staging = self.repo.staging] + and p.repo.staging == self.repo.staging] if len(pkgs) > 0: pkg = pkgs[0] deps.append({'dep': dep, 'pkg': pkg}) -- cgit v1.2.3-2-g168b From 5e85c5ac9ed09551d65ec07767094770d248f3b1 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Wed, 29 Jun 2011 10:52:30 -0500 Subject: Move set_created_field() to shared utils class Signed-off-by: Dan McGee --- main/utils.py | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'main') diff --git a/main/utils.py b/main/utils.py index 12d12503..2ca69bcb 100644 --- a/main/utils.py +++ b/main/utils.py @@ -2,6 +2,9 @@ try: import cPickle as pickle except ImportError: import pickle + +from datetime import datetime + from django.core.cache import cache from django.utils.hashcompat import md5_constructor @@ -81,4 +84,11 @@ def retrieve_latest(sender): pass return None +def set_created_field(sender, **kwargs): + '''This will set the 'created' field on any object to datetime.utcnow() if + it is unset. For use as a pre_save signal handler.''' + obj = kwargs['instance'] + if hasattr(obj, 'created') and not obj.created: + obj.created = datetime.utcnow() + # vim: set ts=4 sw=4 et: -- cgit v1.2.3-2-g168b From e37d465c4eb03259b3ce0c89e43c2c21badb0063 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Fri, 1 Jul 2011 18:59:28 -0500 Subject: Allow more lenient data entry for PGP key field Don't allow the max_length parameter to make it through to the HTML form, silently cutting off HTML cut and pastes. Trim out spaces automatically, as well as '0x' and '2048R/' type prefixes. Signed-off-by: Dan McGee --- .../0051_auto__chg_field_userprofile_pgp_key.py | 160 +++++++++++++++++++++ main/models.py | 21 ++- 2 files changed, 180 insertions(+), 1 deletion(-) create mode 100644 main/migrations/0051_auto__chg_field_userprofile_pgp_key.py (limited to 'main') diff --git a/main/migrations/0051_auto__chg_field_userprofile_pgp_key.py b/main/migrations/0051_auto__chg_field_userprofile_pgp_key.py new file mode 100644 index 00000000..6b8abf6e --- /dev/null +++ b/main/migrations/0051_auto__chg_field_userprofile_pgp_key.py @@ -0,0 +1,160 @@ +# encoding: utf-8 +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + +class Migration(SchemaMigration): + + def forwards(self, orm): + db.alter_column('user_profiles', 'pgp_key', self.gf('main.models.PGPKeyField')(max_length=40, null=True)) + + + def backwards(self, orm): + db.alter_column('user_profiles', 'pgp_key', self.gf('django.db.models.fields.CharField')(max_length=40, null=True)) + + 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.donor': { + 'Meta': {'ordering': "['name']", 'object_name': 'Donor', 'db_table': "'donors'"}, + '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'}) + }, + 'main.package': { + 'Meta': {'ordering': "('pkgname',)", '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.models.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.models.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'}), + 'pkgbase': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'pkgdesc': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}), + 'pkgname': ('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', [], {'related_name': "'packages'", 'to': "orm['main.Repo']"}), + 'url': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}) + }, + 'main.packagedepend': { + 'Meta': {'object_name': 'PackageDepend', 'db_table': "'package_depends'"}, + 'depname': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'depvcmp': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'optional': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'pkg': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Package']"}) + }, + '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'}), + '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': "orm['main.Package']"}) + }, + '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'}) + }, + 'main.signoff': { + 'Meta': {'object_name': 'Signoff'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'packager': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}), + 'pkg': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Package']"}), + 'pkgrel': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'pkgver': ('django.db.models.fields.CharField', [], {'max_length': '255'}) + }, + 'main.todolist': { + 'Meta': {'object_name': 'Todolist', 'db_table': "'todolists'"}, + 'creator': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}), + 'date_added': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}) + }, + 'main.todolistpkg': { + 'Meta': {'unique_together': "(('list', 'pkg'),)", 'object_name': 'TodolistPkg', 'db_table': "'todolist_pkgs'"}, + 'complete': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'list': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Todolist']"}), + 'pkg': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Package']"}) + }, + 'main.userprofile': { + 'Meta': {'object_name': 'UserProfile', 'db_table': "'user_profiles'"}, + 'alias': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'allowed_repos': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Repo']", 'symmetrical': 'False', 'blank': 'True'}), + 'favorite_distros': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'interests': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'languages': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'location': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'notify': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'occupation': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'other_contact': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'pgp_key': ('main.models.PGPKeyField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'}), + 'picture': ('django.db.models.fields.files.FileField', [], {'default': "'devs/silhouette.png'", 'max_length': '100'}), + 'public_email': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'roles': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'time_zone': ('django.db.models.fields.CharField', [], {'default': "'UTC'", 'max_length': '100'}), + 'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'userprofile'", 'unique': 'True', 'to': "orm['auth.User']"}), + 'website': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), + 'yob': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}) + } + } + + complete_apps = ['main'] diff --git a/main/models.py b/main/models.py index 9010817b..2f549081 100644 --- a/main/models.py +++ b/main/models.py @@ -22,6 +22,25 @@ class PositiveBigIntegerField(models.BigIntegerField): defaults.update(kwargs) return super(PositiveBigIntegerField, self).formfield(**defaults) +class PGPKeyField(models.CharField): + _south_introspects = True + + def to_python(self, value): + if value == '': + return None + value = super(PGPKeyField, self).to_python(value) + # remove all spaces + value = value.replace(' ', '') + # prune prefixes, either 0x or 2048R/ type + if value.startswith('0x'): + value = value[2:] + value = value.split('/')[-1] + return value + + def formfield(self, **kwargs): + # override so we don't set max_length form field attribute + return models.Field.formfield(self, **kwargs) + def validate_pgp_key_length(value): if len(value) not in (8, 16, 40): raise ValidationError( @@ -45,7 +64,7 @@ class UserProfile(models.Model): max_length=50, help_text="Required field") other_contact = models.CharField(max_length=100, null=True, blank=True) - pgp_key = models.CharField(max_length=40, null=True, blank=True, + pgp_key = PGPKeyField(max_length=40, null=True, blank=True, verbose_name="PGP key", validators=[RegexValidator(r'^[0-9A-F]+$', "Ensure this value consists of only hex characters.", 'hex_char'), validate_pgp_key_length], -- cgit v1.2.3-2-g168b From c5308b75837dfcbb851bcf9e93553c72e4b8996e Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Wed, 29 Jun 2011 12:05:07 -0500 Subject: Recent updates refactor Pull out a few helpful objects and functions for use later elsewhere. Signed-off-by: Dan McGee --- main/utils.py | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) (limited to 'main') diff --git a/main/utils.py b/main/utils.py index 2ca69bcb..81f689e7 100644 --- a/main/utils.py +++ b/main/utils.py @@ -91,4 +91,39 @@ def set_created_field(sender, **kwargs): if hasattr(obj, 'created') and not obj.created: obj.created = datetime.utcnow() +def groupby_preserve_order(iterable, keyfunc): + '''Take an iterable and regroup using keyfunc to determine whether items + belong to the same group. The order of the iterable is preserved and + similar keys do not have to be consecutive. This means the earliest + occurrence of a given key will determine the order of the lists in the + returned list.''' + seen_keys = {} + result = [] + for item in iterable: + key = keyfunc(item) + + group = seen_keys.get(key, None) + if group is None: + group = [] + seen_keys[key] = group + result.append(group) + + group.append(item) + + return result + +class PackageStandin(object): + '''Resembles a Package object, and has a few of the same fields, but is + really a link to a pkgbase that has no package with matching pkgname.''' + def __init__(self, package): + self.package = package + self.pkgname = package.pkgbase + + def __getattr__(self, name): + return getattr(self.package, name) + + def get_absolute_url(self): + return '/packages/%s/%s/%s/' % ( + self.repo.name.lower(), self.arch.name, self.pkgbase) + # vim: set ts=4 sw=4 et: -- cgit v1.2.3-2-g168b From f95abca269aec1409ec1e57de4c6cb5ba1da6369 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Wed, 6 Jul 2011 12:05:19 -0500 Subject: Refactor code to use new signoff model This moves signoff creation and display to the new packages.Signoff model. Signed-off-by: Dan McGee --- main/models.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) (limited to 'main') diff --git a/main/models.py b/main/models.py index 2f549081..473144da 100644 --- a/main/models.py +++ b/main/models.py @@ -6,6 +6,7 @@ from django.forms import ValidationError from main.utils import cache_function, make_choice from packages.models import PackageRelation +from packages.models import Signoff as PackageSignoff from datetime import datetime from itertools import groupby @@ -206,16 +207,13 @@ class Package(models.Model): @property def signoffs(self): - if 'signoffs_cache' in dir(self): - return self.signoffs_cache - self.signoffs_cache = list(Signoff.objects.filter( - pkg=self, - pkgver=self.pkgver, - pkgrel=self.pkgrel)) - return self.signoffs_cache + return PackageSignoff.objects.select_related('user').filter( + pkgbase=self.pkgbase, pkgver=self.pkgver, pkgrel=self.pkgrel, + epoch=self.epoch, arch=self.arch, repo=self.repo) def approved_for_signoff(self): - return len(self.signoffs) >= 2 + count = self.signoffs.filter(revoked__isnull=True).count() + return count >= PackageSignoff.REQUIRED @cache_function(300) def applicable_arches(self): -- cgit v1.2.3-2-g168b From d8e089fd30ac2dacac54c444cc522ee44ee332ed Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Wed, 6 Jul 2011 12:08:42 -0500 Subject: Delete old signoff model Signed-off-by: Dan McGee --- main/migrations/0052_auto__del_signoff.py | 166 ++++++++++++++++++++++++++++++ main/models.py | 6 -- 2 files changed, 166 insertions(+), 6 deletions(-) create mode 100644 main/migrations/0052_auto__del_signoff.py (limited to 'main') diff --git a/main/migrations/0052_auto__del_signoff.py b/main/migrations/0052_auto__del_signoff.py new file mode 100644 index 00000000..8da6becc --- /dev/null +++ b/main/migrations/0052_auto__del_signoff.py @@ -0,0 +1,166 @@ +# encoding: utf-8 +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + +class Migration(SchemaMigration): + + depends_on = ( + ('packages', '0008_add_signoff_model'), + ) + + def forwards(self, orm): + # Deleting model 'Signoff' + db.delete_table('main_signoff') + + + def backwards(self, orm): + # Adding model 'Signoff' + db.create_table('main_signoff', ( + ('pkgrel', self.gf('django.db.models.fields.CharField')(max_length=255)), + ('packager', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'])), + ('pkg', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['main.Package'])), + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('pkgver', self.gf('django.db.models.fields.CharField')(max_length=255)), + )) + db.send_create_signal('main', ['Signoff']) + + + 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.donor': { + 'Meta': {'ordering': "['name']", 'object_name': 'Donor', 'db_table': "'donors'"}, + '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'}) + }, + 'main.package': { + 'Meta': {'ordering': "('pkgname',)", '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.models.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.models.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'}), + 'pkgbase': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'pkgdesc': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}), + 'pkgname': ('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', [], {'related_name': "'packages'", 'to': "orm['main.Repo']"}), + 'url': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}) + }, + 'main.packagedepend': { + 'Meta': {'object_name': 'PackageDepend', 'db_table': "'package_depends'"}, + 'depname': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), + 'depvcmp': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '255'}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'optional': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'pkg': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Package']"}) + }, + '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'}), + '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': "orm['main.Package']"}) + }, + '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'}) + }, + 'main.todolist': { + 'Meta': {'object_name': 'Todolist', 'db_table': "'todolists'"}, + 'creator': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}), + 'date_added': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}) + }, + 'main.todolistpkg': { + 'Meta': {'unique_together': "(('list', 'pkg'),)", 'object_name': 'TodolistPkg', 'db_table': "'todolist_pkgs'"}, + 'complete': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'list': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Todolist']"}), + 'pkg': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Package']"}) + }, + 'main.userprofile': { + 'Meta': {'object_name': 'UserProfile', 'db_table': "'user_profiles'"}, + 'alias': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'allowed_repos': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Repo']", 'symmetrical': 'False', 'blank': 'True'}), + 'favorite_distros': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'interests': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'languages': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'location': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'notify': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'occupation': ('django.db.models.fields.CharField', [], {'max_length': '50', 'null': 'True', 'blank': 'True'}), + 'other_contact': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'pgp_key': ('main.models.PGPKeyField', [], {'max_length': '40', 'null': 'True', 'blank': 'True'}), + 'picture': ('django.db.models.fields.files.FileField', [], {'default': "'devs/silhouette.png'", 'max_length': '100'}), + 'public_email': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + 'roles': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'time_zone': ('django.db.models.fields.CharField', [], {'default': "'UTC'", 'max_length': '100'}), + 'user': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'userprofile'", 'unique': 'True', 'to': "orm['auth.User']"}), + 'website': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), + 'yob': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}) + } + } + + complete_apps = ['main'] diff --git a/main/models.py b/main/models.py index 473144da..6e134810 100644 --- a/main/models.py +++ b/main/models.py @@ -367,12 +367,6 @@ class Package(models.Model): pkgname=self.pkgname).exclude(id=self.id).order_by( 'arch__name', 'repo__name') -class Signoff(models.Model): - pkg = models.ForeignKey(Package) - pkgver = models.CharField(max_length=255) - pkgrel = models.CharField(max_length=255) - packager = models.ForeignKey(User) - class PackageFile(models.Model): pkg = models.ForeignKey(Package) is_directory = models.BooleanField(default=False) -- cgit v1.2.3-2-g168b From 2a73675f0ed894d57ed13c5799e988f8d10ccfe2 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Fri, 29 Jul 2011 23:08:33 -0500 Subject: Select arch/repo for split package related fields Signed-off-by: Dan McGee --- main/models.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'main') diff --git a/main/models.py b/main/models.py index 6e134810..70372823 100644 --- a/main/models.py +++ b/main/models.py @@ -307,12 +307,12 @@ class Package(models.Model): """ try: # start by looking for something in this repo - return Package.objects.get(arch=self.arch, + return Package.objects.normal().get(arch=self.arch, repo=self.repo, pkgname=self.pkgbase) except Package.DoesNotExist: # this package might be split across repos? just find one # that matches the correct [testing] repo flag - pkglist = Package.objects.filter(arch=self.arch, + pkglist = Package.objects.normal().filter(arch=self.arch, repo__testing=self.repo.testing, repo__staging=self.repo.staging, pkgname=self.pkgbase) if len(pkglist) > 0: @@ -327,7 +327,7 @@ class Package(models.Model): repo.testing and repo.staging flags. For any non-split packages, the return value will be an empty list. """ - return Package.objects.filter(arch__in=self.applicable_arches(), + return Package.objects.normal().filter(arch__in=self.applicable_arches(), repo__testing=self.repo.testing, repo__staging=self.repo.staging, pkgbase=self.pkgbase).exclude(id=self.id) -- cgit v1.2.3-2-g168b