diff --git a/badger/admin.py b/badger/admin.py
index 4ed3eb7..bb49805 100644
--- a/badger/admin.py
+++ b/badger/admin.py
@@ -4,7 +4,12 @@ from django.contrib import admin
from django import forms
from django.db import models
-from .models import (Badge, Award, Progress)
+try:
+ from funfactory.urlresolvers import reverse
+except ImportError, e:
+ from django.core.urlresolvers import reverse
+
+from .models import (Badge, Award, Progress, DeferredAward)
UPLOADS_URL = getattr(settings, 'BADGER_UPLOADS_URL',
@@ -20,16 +25,49 @@ def show_image(obj):
img_url = "%s%s" % (UPLOADS_URL, obj.image)
return ('' %
(img_url, img_url))
+
show_image.allow_tags = True
show_image.short_description = "Image"
-class BadgerAdmin(admin.ModelAdmin):
+def build_related_link(self, model_name, name_single, name_plural, qs):
+ link = '%s?%s' % (
+ reverse('admin:badger_%s_changelist' % model_name, args=[]),
+ 'badge__exact=%s' % (self.id)
+ )
+ new_link = '%s?%s' % (
+ reverse('admin:badger_%s_add' % model_name, args=[]),
+ 'badge=%s' % (self.id)
+ )
+ count = qs.count()
+ what = (count == 1) and name_single or name_plural
+ return ('%s %s (new)' %
+ (link, count, what, new_link))
- list_display = ("title", "slug", "unique", "creator", show_image, "created", )
+def related_deferredawards_link(self):
+ return build_related_link(self, 'deferredaward', 'deferred', 'deferred',
+ self.deferredaward_set)
+
+related_deferredawards_link.allow_tags = True
+related_deferredawards_link.short_description = "Deferred Awards"
+
+
+def related_awards_link(self):
+ return build_related_link(self, 'award', 'award', 'awards',
+ self.award_set)
+
+related_awards_link.allow_tags = True
+related_awards_link.short_description = "Awards"
+
+
+class BadgeAdmin(admin.ModelAdmin):
+ list_display = ("id", "title", show_image, "slug", "unique", "creator",
+ related_awards_link, related_deferredawards_link, "created",)
+ list_display_links = ('id', 'title',)
+ search_fields = ("title", "slug", "image", "description",)
filter_horizontal = ('prerequisites', )
-
+ prepopulated_fields = {"slug": ("title",)}
formfield_overrides = {
models.ManyToManyField: {
"widget": forms.widgets.SelectMultiple(attrs={"size": 25})
@@ -37,17 +75,44 @@ class BadgerAdmin(admin.ModelAdmin):
}
+def badge_link(self):
+ url = reverse('admin:badger_badge_change', args=[self.badge.id])
+ return '%s' % (url, self.badge)
+
+badge_link.allow_tags = True
+badge_link.short_description = 'Badge'
+
+
class AwardAdmin(admin.ModelAdmin):
-
- list_display = (show_unicode, 'badge', 'user', 'creator', show_image, 'created', )
-
+ list_display = (show_unicode, badge_link, show_image, 'user', 'creator',
+ 'created', )
fields = ('badge', 'user', 'creator', )
+ search_fields = ("badge__title", "badge__slug", "badge__description",)
class ProgressAdmin(admin.ModelAdmin):
pass
-admin.site.register(Badge, BadgerAdmin)
-admin.site.register(Award, AwardAdmin)
-admin.site.register(Progress, ProgressAdmin)
+def claim_code_link(self):
+ return '%s' % (self.get_claim_url(), self.claim_code)
+
+claim_code_link.allow_tags = True
+claim_code_link.short_description = "Claim Code"
+
+
+class DeferredAwardAdmin(admin.ModelAdmin):
+ list_display = ('id', claim_code_link, badge_link, 'email', 'reusable',
+ 'creator', 'created', 'modified',)
+ list_display_links = ('id',)
+ list_filter = ('reusable', )
+ fields = ('badge', 'claim_code', 'email', 'reusable', 'description',)
+ readonly_fields = ('created', 'modified')
+ search_fields = ("badge__title", "badge__slug", "badge__description",)
+
+
+for x in ((Badge, BadgeAdmin),
+ (Award, AwardAdmin),
+ (Progress, ProgressAdmin),
+ (DeferredAward, DeferredAwardAdmin),):
+ admin.site.register(*x)
diff --git a/badger/helpers.py b/badger/helpers.py
index 6e359c2..b46afe7 100644
--- a/badger/helpers.py
+++ b/badger/helpers.py
@@ -5,6 +5,7 @@ from django.conf import settings
from django.contrib.auth.models import SiteProfileNotAvailable
from django.core.exceptions import ObjectDoesNotExist
+from django.utils.html import conditional_escape
try:
from commons.urlresolvers import reverse
@@ -61,3 +62,13 @@ def user_awards(user):
@register.function
def user_badges(user):
return Badge.objects.filter(creator=user)
+
+
+@register.function
+def qr_code_image(value, alt=None, size=150):
+ url = conditional_escape("http://chart.apis.google.com/chart?%s" % \
+ urllib.urlencode({'chs':'%sx%s' % (size, size), 'cht':'qr', 'chl':value, 'choe':'UTF-8'}))
+ alt = conditional_escape(alt or value)
+
+ return Markup(u"""""" %
+ (url, size, size, alt))
diff --git a/badger/models.py b/badger/models.py
index cc77624..008bf62 100644
--- a/badger/models.py
+++ b/badger/models.py
@@ -267,21 +267,31 @@ class Badge(models.Model):
"""Representation of a badge"""
objects = BadgeManager()
- title = models.CharField(max_length=255, blank=False, unique=True)
- slug = models.SlugField(blank=False, unique=True)
- description = models.TextField(blank=True)
+ title = models.CharField(max_length=255, blank=False, unique=True,
+ help_text="Short, descriptive title")
+ slug = models.SlugField(blank=False, unique=True,
+ help_text="Very short name, for use in URLs and links")
+ description = models.TextField(blank=True,
+ help_text="Longer description of the badge and its criteria")
image = models.ImageField(blank=True, null=True,
- storage=BADGE_UPLOADS_FS,
- upload_to=mk_upload_to('image','png'))
+ storage=BADGE_UPLOADS_FS, upload_to=mk_upload_to('image','png'),
+ help_text="Upload an image to represent the badge")
prerequisites = models.ManyToManyField('self', symmetrical=False,
- blank=True, null=True)
- unique = models.BooleanField(default=False)
+ blank=True, null=True,
+ help_text="When all of the selected badges have been awarded, this "
+ "badge will be automatically awarded.")
+ # 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=False,
+ help_text="Should only one award of this badge be allowed per "
+ "person?")
creator = models.ForeignKey(User, blank=True, null=True)
created = models.DateTimeField(auto_now_add=True, blank=False)
modified = models.DateTimeField(auto_now=True, blank=False)
class Meta:
unique_together = ('title', 'slug')
+ ordering = ['-modified', '-created']
get_permissions_for = get_permissions_for
@@ -457,6 +467,9 @@ class Award(models.Model):
get_permissions_for = get_permissions_for
+ class Meta:
+ ordering = ['-modified', '-created']
+
def __unicode__(self):
by = self.creator and (' by %s' % self.creator) or ''
return u'Award of %s to %s%s' % (self.badge, self.user, by)
@@ -682,12 +695,14 @@ class DeferredAward(models.Model):
reusable = models.BooleanField(default=False)
email = models.EmailField(blank=True, null=True, db_index=True)
claim_code = models.CharField(max_length=CLAIM_CODE_LENGTH,
- default=make_random_code, editable=False, unique=True,
- db_index=True)
+ default=make_random_code, unique=True, db_index=True)
creator = models.ForeignKey(User, blank=True, null=True)
created = models.DateTimeField(auto_now_add=True, blank=False)
modified = models.DateTimeField(auto_now=True, blank=False)
+ class Meta:
+ ordering = ['-modified', '-created']
+
def get_claim_url(self):
"""Get the URL to a page where this DeferredAward can be claimed."""
return reverse('badger.views.claim_deferred_award',
diff --git a/badger/urls.py b/badger/urls.py
index cc1a30f..21f1881 100644
--- a/badger/urls.py
+++ b/badger/urls.py
@@ -20,6 +20,8 @@ urlpatterns = patterns('badger.views',
name='badger.award_detail'),
url(r'^claim/(?P[^/]+)/?$', 'claim_deferred_award',
name='badger.claim_deferred_award'),
+ url(r'^claim/?$', 'claim_deferred_award',
+ name='badger.claim_deferred_award_form'),
url(r'^badge/(?P[^/]+)/award', 'award_badge',
name='badger.award_badge'),
url(r'^badge/(?P[^\.]+)\.json$', 'detail',
diff --git a/badger/views.py b/badger/views.py
index 91b4bf7..351b82a 100644
--- a/badger/views.py
+++ b/badger/views.py
@@ -163,8 +163,10 @@ def award_detail(request, slug, id, format="html"):
@require_http_methods(['GET', 'POST'])
@login_required
-def claim_deferred_award(request, claim_code):
+def claim_deferred_award(request, claim_code=None):
"""Deferred award detail view"""
+ if not claim_code:
+ claim_code = request.GET.get('code', '').strip()
deferred_award = get_object_or_404(DeferredAward, claim_code=claim_code)
if request.method == "POST":