From d21d8be0186413fe1fa5fd6c859786465472ee10 Mon Sep 17 00:00:00 2001 From: Dan McGee Date: Fri, 20 Apr 2012 10:21:28 -0500 Subject: UserProfile model and fields shuffle Move this model into the devel/ application, and move the PGPKeyField which is used only by these models into the application as well. This involves updating some old migrations along the way to ensure we don't reference a field class that no longer exists. Signed-off-by: Dan McGee --- devel/admin.py | 18 +++- devel/fields.py | 30 +++++++ devel/management/commands/generate_keyring.py | 3 +- devel/migrations/0002_auto__add_masterkey.py | 4 +- devel/migrations/0003_auto__add_pgpsignature.py | 10 +-- devel/migrations/0004_masterkey_dates.py | 6 +- devel/migrations/0005_auto__add_userprofile.py | 113 ++++++++++++++++++++++++ devel/models.py | 60 ++++++++++++- devel/tests.py | 6 +- devel/views.py | 3 +- 10 files changed, 235 insertions(+), 18 deletions(-) create mode 100644 devel/fields.py create mode 100644 devel/migrations/0005_auto__add_userprofile.py (limited to 'devel') diff --git a/devel/admin.py b/devel/admin.py index 717ba1b..5a704c0 100644 --- a/devel/admin.py +++ b/devel/admin.py @@ -1,6 +1,18 @@ from django.contrib import admin +from django.contrib.auth.admin import UserAdmin +from django.contrib.auth.models import User -from .models import MasterKey, PGPSignature +from .models import UserProfile, MasterKey, PGPSignature + + +class UserProfileInline(admin.StackedInline): + model = UserProfile + + +class UserProfileAdmin(UserAdmin): + inlines = [UserProfileInline] + list_display = ('username', 'email', 'first_name', 'last_name', 'is_staff', 'is_active') + list_filter = ('is_staff', 'is_superuser', 'is_active') class MasterKeyAdmin(admin.ModelAdmin): @@ -8,6 +20,7 @@ class MasterKeyAdmin(admin.ModelAdmin): search_fields = ('pgp_key', 'owner', 'revoker') date_hierarchy = 'created' + class PGPSignatureAdmin(admin.ModelAdmin): list_display = ('signer', 'signee', 'created', 'expires', 'valid') list_filter = ('valid',) @@ -15,6 +28,9 @@ class PGPSignatureAdmin(admin.ModelAdmin): date_hierarchy = 'created' +admin.site.unregister(User) +admin.site.register(User, UserProfileAdmin) + admin.site.register(MasterKey, MasterKeyAdmin) admin.site.register(PGPSignature, PGPSignatureAdmin) diff --git a/devel/fields.py b/devel/fields.py new file mode 100644 index 0000000..606ca63 --- /dev/null +++ b/devel/fields.py @@ -0,0 +1,30 @@ +from django.db import models +from django.core.validators import RegexValidator + + +class PGPKeyField(models.CharField): + _south_introspects = True + + def __init__(self, *args, **kwargs): + super(PGPKeyField, self).__init__(*args, **kwargs) + self.validators.append(RegexValidator(r'^[0-9A-F]{40}$', + "Ensure this value consists of 40 hex characters.", 'hex_char')) + + def to_python(self, value): + if value == '' or value is None: + 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] + # make all (hex letters) uppercase + return value.upper() + + def formfield(self, **kwargs): + # override so we don't set max_length form field attribute + return models.Field.formfield(self, **kwargs) + +# vim: set ts=4 sw=4 et: diff --git a/devel/management/commands/generate_keyring.py b/devel/management/commands/generate_keyring.py index 062c738..b9117c8 100644 --- a/devel/management/commands/generate_keyring.py +++ b/devel/management/commands/generate_keyring.py @@ -13,8 +13,7 @@ import logging import subprocess import sys -from devel.models import MasterKey -from main.models import UserProfile +from devel.models import MasterKey, UserProfile logging.basicConfig( level=logging.INFO, diff --git a/devel/migrations/0002_auto__add_masterkey.py b/devel/migrations/0002_auto__add_masterkey.py index ac1f745..ba9a3e5 100644 --- a/devel/migrations/0002_auto__add_masterkey.py +++ b/devel/migrations/0002_auto__add_masterkey.py @@ -15,7 +15,7 @@ class Migration(SchemaMigration): ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), ('owner', self.gf('django.db.models.fields.related.ForeignKey')(related_name='masterkey_owner', to=orm['auth.User'])), ('revoker', self.gf('django.db.models.fields.related.ForeignKey')(related_name='masterkey_revoker', to=orm['auth.User'])), - ('pgp_key', self.gf('main.fields.PGPKeyField')(max_length=40)), + ('pgp_key', self.gf('devel.fields.PGPKeyField')(max_length=40)), ('created', self.gf('django.db.models.fields.DateTimeField')()), ('revoked', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True)), )) @@ -67,7 +67,7 @@ class Migration(SchemaMigration): 'created': ('django.db.models.fields.DateTimeField', [], {}), 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 'owner': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'masterkey_owner'", 'to': "orm['auth.User']"}), - 'pgp_key': ('main.fields.PGPKeyField', [], {'max_length': '40'}), + 'pgp_key': ('devel.fields.PGPKeyField', [], {'max_length': '40'}), 'revoked': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), 'revoker': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'masterkey_revoker'", 'to': "orm['auth.User']"}) } diff --git a/devel/migrations/0003_auto__add_pgpsignature.py b/devel/migrations/0003_auto__add_pgpsignature.py index f9ac502..e16de1c 100644 --- a/devel/migrations/0003_auto__add_pgpsignature.py +++ b/devel/migrations/0003_auto__add_pgpsignature.py @@ -8,8 +8,8 @@ class Migration(SchemaMigration): def forwards(self, orm): db.create_table('devel_pgpsignature', ( ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), - ('signer', self.gf('main.fields.PGPKeyField')(max_length=40)), - ('signee', self.gf('main.fields.PGPKeyField')(max_length=40)), + ('signer', self.gf('devel.fields.PGPKeyField')(max_length=40)), + ('signee', self.gf('devel.fields.PGPKeyField')(max_length=40)), ('created', self.gf('django.db.models.fields.DateField')()), ('expires', self.gf('django.db.models.fields.DateField')(null=True)), ('valid', self.gf('django.db.models.fields.BooleanField')(default=True)), @@ -63,7 +63,7 @@ class Migration(SchemaMigration): 'created': ('django.db.models.fields.DateTimeField', [], {}), 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 'owner': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'masterkey_owner'", 'to': "orm['auth.User']"}), - 'pgp_key': ('main.fields.PGPKeyField', [], {'max_length': '40'}), + 'pgp_key': ('devel.fields.PGPKeyField', [], {'max_length': '40'}), 'revoked': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), 'revoker': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'masterkey_revoker'", 'to': "orm['auth.User']"}) }, @@ -72,8 +72,8 @@ class Migration(SchemaMigration): 'created': ('django.db.models.fields.DateField', [], {}), 'expires': ('django.db.models.fields.DateField', [], {'null': 'True'}), 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'signee': ('main.fields.PGPKeyField', [], {'max_length': '40'}), - 'signer': ('main.fields.PGPKeyField', [], {'max_length': '40'}), + 'signee': ('devel.fields.PGPKeyField', [], {'max_length': '40'}), + 'signer': ('devel.fields.PGPKeyField', [], {'max_length': '40'}), 'valid': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) } } diff --git a/devel/migrations/0004_masterkey_dates.py b/devel/migrations/0004_masterkey_dates.py index dc7750d..f2020dd 100644 --- a/devel/migrations/0004_masterkey_dates.py +++ b/devel/migrations/0004_masterkey_dates.py @@ -56,7 +56,7 @@ class Migration(SchemaMigration): 'created': ('django.db.models.fields.DateField', [], {}), 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 'owner': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'masterkey_owner'", 'to': "orm['auth.User']"}), - 'pgp_key': ('main.fields.PGPKeyField', [], {'max_length': '40'}), + 'pgp_key': ('devel.fields.PGPKeyField', [], {'max_length': '40'}), 'revoked': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), 'revoker': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'masterkey_revoker'", 'to': "orm['auth.User']"}) }, @@ -65,8 +65,8 @@ class Migration(SchemaMigration): 'created': ('django.db.models.fields.DateField', [], {}), 'expires': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'signee': ('main.fields.PGPKeyField', [], {'max_length': '40'}), - 'signer': ('main.fields.PGPKeyField', [], {'max_length': '40'}), + 'signee': ('devel.fields.PGPKeyField', [], {'max_length': '40'}), + 'signer': ('devel.fields.PGPKeyField', [], {'max_length': '40'}), 'valid': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) } } diff --git a/devel/migrations/0005_auto__add_userprofile.py b/devel/migrations/0005_auto__add_userprofile.py new file mode 100644 index 0000000..ff6f978 --- /dev/null +++ b/devel/migrations/0005_auto__add_userprofile.py @@ -0,0 +1,113 @@ +# -*- coding: utf-8 -*- +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + depends_on = ( + ('main', '0059_auto__del_userprofile'), + ) + + def forwards(self, orm): + if not db.dry_run: + db.send_create_signal('devel', ['UserProfile']) + orm['contenttypes.ContentType'].objects.filter( + app_label='main', model='userprofile').update( + app_label='devel') + + def backwards(self, orm): + pass + + 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'}) + }, + 'devel.masterkey': { + 'Meta': {'ordering': "('created',)", 'object_name': 'MasterKey'}, + 'created': ('django.db.models.fields.DateField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'masterkey_owner'", 'to': "orm['auth.User']"}), + 'pgp_key': ('devel.fields.PGPKeyField', [], {'max_length': '40'}), + 'revoked': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'revoker': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'masterkey_revoker'", 'to': "orm['auth.User']"}) + }, + 'devel.pgpsignature': { + 'Meta': {'object_name': 'PGPSignature'}, + 'created': ('django.db.models.fields.DateField', [], {}), + 'expires': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'signee': ('devel.fields.PGPKeyField', [], {'max_length': '40'}), + 'signer': ('devel.fields.PGPKeyField', [], {'max_length': '40'}), + 'valid': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'devel.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'}), + 'latin_name': ('django.db.models.fields.CharField', [], {'max_length': '255', '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': ('devel.fields.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'}) + }, + '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'}), + '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 = ['devel'] diff --git a/devel/models.py b/devel/models.py index 2fc6106..79c56f2 100644 --- a/devel/models.py +++ b/devel/models.py @@ -1,8 +1,66 @@ # -*- coding: utf-8 -*- +import pytz + from django.db import models from django.contrib.auth.models import User -from main.fields import PGPKeyField +from .fields import PGPKeyField +from main.utils import make_choice + + +class UserProfile(models.Model): + notify = models.BooleanField( + "Send notifications", + default=True, + help_text="When enabled, send user 'flag out-of-date' notifications") + time_zone = models.CharField( + max_length=100, + choices=make_choice(pytz.common_timezones), + default="UTC", + help_text="Used for developer clock page") + alias = models.CharField( + max_length=50, + help_text="Required field") + public_email = models.CharField( + max_length=50, + help_text="Required field") + other_contact = models.CharField(max_length=100, null=True, blank=True) + pgp_key = PGPKeyField(max_length=40, null=True, blank=True, + verbose_name="PGP key fingerprint", + help_text="consists of 40 hex digits; use `gpg --fingerprint`") + website = models.CharField(max_length=200, null=True, blank=True) + yob = models.IntegerField("Year of birth", null=True, blank=True) + location = models.CharField(max_length=50, null=True, blank=True) + languages = models.CharField(max_length=50, null=True, blank=True) + interests = models.CharField(max_length=255, null=True, blank=True) + occupation = models.CharField(max_length=50, null=True, blank=True) + roles = models.CharField(max_length=255, null=True, blank=True) + favorite_distros = models.CharField(max_length=255, null=True, blank=True) + picture = models.FileField(upload_to='devs', default='devs/silhouette.png', + help_text="Ideally 125px by 125px") + user = models.OneToOneField(User, related_name='userprofile') + allowed_repos = models.ManyToManyField('main.Repo', blank=True) + latin_name = models.CharField(max_length=255, null=True, blank=True, + help_text="Latin-form name; used only for non-Latin full names") + + class Meta: + db_table = 'user_profiles' + verbose_name = 'Additional Profile Data' + verbose_name_plural = 'Additional Profile Data' + + def get_absolute_url(self): + # TODO: this is disgusting. find a way to consolidate this logic with + # public.views.userlist among other places, and make some constants or + # something so we aren't using copies of string names everywhere. + group_names = self.user.groups.values_list('name', flat=True) + if "Developers" in group_names: + prefix = "developers" + elif "Trusted Users" in group_names: + prefix = "trustedusers" + else: + prefix = "fellows" + return '/%s/#%s' % (prefix, self.user.username) + class MasterKey(models.Model): diff --git a/devel/tests.py b/devel/tests.py index 01eed0f..5c736a3 100644 --- a/devel/tests.py +++ b/devel/tests.py @@ -1,8 +1,8 @@ +from django.contrib.auth.models import User from django.test import TestCase -from django.contrib.auth.models import User -from devel.utils import UserFinder -from main.models import UserProfile +from .utils import UserFinder +from .models import UserProfile class DevelTest(TestCase): def test_index(self): diff --git a/devel/views.py b/devel/views.py index 88e76be..d2ce65d 100644 --- a/devel/views.py +++ b/devel/views.py @@ -22,8 +22,9 @@ from django.views.decorators.cache import never_cache from django.views.generic.simple import direct_to_template from django.utils.http import http_date +from .models import UserProfile from main.models import Package, PackageDepend, PackageFile, TodolistPkg -from main.models import Arch, Repo, UserProfile +from main.models import Arch, Repo from main.utils import utc_now from packages.models import PackageRelation from packages.utils import get_signoff_groups -- cgit v1.2.3-24-g4f1b