diff --git a/badger/migrations/0008_auto__add_field_badge_prerequisites_number.py b/badger/migrations/0008_auto__add_field_badge_prerequisites_number.py new file mode 100644 index 0000000..5ec5bf7 --- /dev/null +++ b/badger/migrations/0008_auto__add_field_badge_prerequisites_number.py @@ -0,0 +1,128 @@ +# -*- coding: 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): + # Adding field 'Badge.prerequisites_number' + db.add_column('badger_badge', 'prerequisites_number', + self.gf('django.db.models.fields.PositiveIntegerField')(null=True, blank=True), + keep_default=False) + + + def backwards(self, orm): + # Deleting field 'Badge.prerequisites_number' + db.delete_column('badger_badge', 'prerequisites_number') + + + 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'}) + }, + 'badger.award': { + 'Meta': {'ordering': "['-modified', '-created']", 'object_name': 'Award'}, + 'badge': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['badger.Badge']"}), + 'claim_code': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '32', 'db_index': 'True', 'blank': 'True'}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'creator': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'award_creator'", 'null': 'True', 'to': "orm['auth.User']"}), + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'hidden': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'award_user'", 'to': "orm['auth.User']"}) + }, + 'badger.badge': { + 'Meta': {'ordering': "['-modified', '-created']", 'unique_together': "(('title', 'slug'),)", 'object_name': 'Badge'}, + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'creator': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'nominations_accepted': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'nominations_autoapproved': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'prerequisites': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['badger.Badge']", 'null': 'True', 'blank': 'True'}), + 'prerequisites_number': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), + 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50'}), + 'title': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'unique': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'badger.deferredaward': { + 'Meta': {'ordering': "['-modified', '-created']", 'object_name': 'DeferredAward'}, + 'badge': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['badger.Badge']"}), + 'claim_code': ('django.db.models.fields.CharField', [], {'default': "'fj347h'", 'unique': 'True', 'max_length': '32', 'db_index': 'True'}), + 'claim_group': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '32', 'null': 'True', 'blank': 'True'}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'creator': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'email': ('django.db.models.fields.EmailField', [], {'db_index': 'True', 'max_length': '75', 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'reusable': ('django.db.models.fields.BooleanField', [], {'default': 'False'}) + }, + 'badger.nomination': { + 'Meta': {'object_name': 'Nomination'}, + 'accepted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'approver': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'nomination_approver'", 'null': 'True', 'to': "orm['auth.User']"}), + 'award': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['badger.Award']", 'null': 'True', 'blank': 'True'}), + 'badge': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['badger.Badge']"}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'creator': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'nomination_creator'", 'null': 'True', 'to': "orm['auth.User']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'nominee': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'nomination_nominee'", 'to': "orm['auth.User']"}), + 'rejected_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'nomination_rejected_by'", 'null': 'True', 'to': "orm['auth.User']"}), + 'rejected_reason': ('django.db.models.fields.TextField', [], {'blank': 'True'}) + }, + 'badger.progress': { + 'Meta': {'unique_together': "(('badge', 'user'),)", 'object_name': 'Progress'}, + 'badge': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['badger.Badge']"}), + 'counter': ('django.db.models.fields.FloatField', [], {'default': '0', 'null': 'True', 'blank': 'True'}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'notes': ('badger.models.JSONField', [], {'null': 'True', 'blank': 'True'}), + 'percent': ('django.db.models.fields.FloatField', [], {'default': '0'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'progress_user'", 'to': "orm['auth.User']"}) + }, + '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'}) + } + } + + complete_apps = ['badger'] \ No newline at end of file diff --git a/badger/migrations/0009_auto__add_field_badge_allows_awarding_badge.py b/badger/migrations/0009_auto__add_field_badge_allows_awarding_badge.py new file mode 100644 index 0000000..ef79343 --- /dev/null +++ b/badger/migrations/0009_auto__add_field_badge_allows_awarding_badge.py @@ -0,0 +1,129 @@ +# -*- coding: 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): + # Adding field 'Badge.allows_awarding_badge' + db.add_column('badger_badge', 'allows_awarding_badge', + self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='+', null=True, to=orm['badger.Badge']), + keep_default=False) + + + def backwards(self, orm): + # Deleting field 'Badge.allows_awarding_badge' + db.delete_column('badger_badge', 'allows_awarding_badge_id') + + + 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'}) + }, + 'badger.award': { + 'Meta': {'ordering': "['-modified', '-created']", 'object_name': 'Award'}, + 'badge': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['badger.Badge']"}), + 'claim_code': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '32', 'db_index': 'True', 'blank': 'True'}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'creator': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'award_creator'", 'null': 'True', 'to': "orm['auth.User']"}), + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'hidden': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'award_user'", 'to': "orm['auth.User']"}) + }, + 'badger.badge': { + 'Meta': {'ordering': "['-modified', '-created']", 'unique_together': "(('title', 'slug'),)", 'object_name': 'Badge'}, + 'allows_awarding_badge': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'to': "orm['badger.Badge']"}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'creator': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'nominations_accepted': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'nominations_autoapproved': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'prerequisites': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['badger.Badge']", 'null': 'True', 'blank': 'True'}), + 'prerequisites_number': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}), + 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '50'}), + 'title': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'unique': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + 'badger.deferredaward': { + 'Meta': {'ordering': "['-modified', '-created']", 'object_name': 'DeferredAward'}, + 'badge': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['badger.Badge']"}), + 'claim_code': ('django.db.models.fields.CharField', [], {'default': "'ynwjmh'", 'unique': 'True', 'max_length': '32', 'db_index': 'True'}), + 'claim_group': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '32', 'null': 'True', 'blank': 'True'}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'creator': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'email': ('django.db.models.fields.EmailField', [], {'db_index': 'True', 'max_length': '75', 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'reusable': ('django.db.models.fields.BooleanField', [], {'default': 'False'}) + }, + 'badger.nomination': { + 'Meta': {'object_name': 'Nomination'}, + 'accepted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'approver': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'nomination_approver'", 'null': 'True', 'to': "orm['auth.User']"}), + 'award': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['badger.Award']", 'null': 'True', 'blank': 'True'}), + 'badge': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['badger.Badge']"}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'creator': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'nomination_creator'", 'null': 'True', 'to': "orm['auth.User']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'nominee': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'nomination_nominee'", 'to': "orm['auth.User']"}), + 'rejected_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'nomination_rejected_by'", 'null': 'True', 'to': "orm['auth.User']"}), + 'rejected_reason': ('django.db.models.fields.TextField', [], {'blank': 'True'}) + }, + 'badger.progress': { + 'Meta': {'unique_together': "(('badge', 'user'),)", 'object_name': 'Progress'}, + 'badge': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['badger.Badge']"}), + 'counter': ('django.db.models.fields.FloatField', [], {'default': '0', 'null': 'True', 'blank': 'True'}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'notes': ('badger.models.JSONField', [], {'null': 'True', 'blank': 'True'}), + 'percent': ('django.db.models.fields.FloatField', [], {'default': '0'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'progress_user'", 'to': "orm['auth.User']"}) + }, + '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'}) + } + } + + complete_apps = ['badger'] \ No newline at end of file diff --git a/badger/models.py b/badger/models.py index cf40323..4ee46d4 100644 --- a/badger/models.py +++ b/badger/models.py @@ -385,10 +385,15 @@ class Badge(models.Model): image = models.ImageField(blank=True, null=True, storage=BADGE_UPLOADS_FS, upload_to=mk_upload_to('image','png'), help_text="Upload an image to represent the badge") + prerequisites_number = models.PositiveIntegerField("Number of prerequisites required", blank=True, null=True, + help_text="This many prerequisites have to be awarded to achieve this badge (0 or blank to require all)") prerequisites = models.ManyToManyField('self', symmetrical=False, blank=True, null=True, - help_text="When all of the selected badges have been awarded, this " + help_text="When the above number of the selected badges have been awarded, this " "badge will be automatically awarded.") + allows_awarding_badge = models.ForeignKey('self', blank=True, null=True, related_name='+', + help_text="Users who have have obtained the selected badge will be able to award the badge you are viewing") + # TODO: Rename? Eventually we'll want a globally-unique badge. That is, one # unique award for one person for the whole site. unique = models.BooleanField(default=True, @@ -485,7 +490,9 @@ def allows_award_to(self, user): return True if user == self.creator: return True - + if self.allows_awarding_badge.is_awarded_to(user): + return True + # TODO: List of delegates for whom awarding is allowed return False @@ -560,17 +567,23 @@ def award_to(self, awardee=None, email=None, awarder=None, description=description) def check_prerequisites(self, awardee, dep_badge, award): - """Check the prerequisites for this badge. If they're all met, award + """Check the prerequisites for this badge. If they're, award this badge to the user.""" if self.is_awarded_to(awardee): # Not unique, but badge auto-award from prerequisites should only # happen once. return None + award_count = 0 for badge in self.prerequisites.all(): - if not badge.is_awarded_to(awardee): - # Bail on the first unmet prerequisites - return None - return self.award_to(awardee) + if badge.is_awarded_to(awardee): + award_count += 1 + if self.prerequisites_number: + if award_count >= self.prerequisites_number: + return self.award_to(awardee) + else: # No pre-requisite number specified; need to check for all pre-requisites + if award_count >= self.prerequisites.all().count(): + return self.award_to(awardee) + return None def is_awarded_to(self, user): """Has this badge been awarded to the user?""" @@ -795,8 +808,8 @@ def as_obi_assertion(self, request=None): "salt": hash_salt, "evidence": urljoin(base_url, self.get_absolute_url()), # TODO: implement award expiration - # "expires": self.expires.strftime('%s'), - "issued_on": self.created.strftime('%s'), + # "expires": self.expires.isoformat(), + "issued_on": self.created.isoformat(), "badge": badge_data } return assertion