Improvements to admin, claim codes, login

* Fix to ?next parameter in post-login redirect

* Simple claim code entry field in the header

* Quick hack to stick a QR code on the DeferredAward detail page.

* Admin improvements to better handle relations between badges, awards,
  and deferred awards.
This commit is contained in:
Les Orchard 2012-04-07 12:51:36 -04:00
Родитель a9a849cb7e
Коммит 85f3dc1c6a
5 изменённых файлов: 115 добавлений и 20 удалений

Просмотреть файл

@ -4,7 +4,12 @@ from django.contrib import admin
from django import forms from django import forms
from django.db import models 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', UPLOADS_URL = getattr(settings, 'BADGER_UPLOADS_URL',
@ -20,16 +25,49 @@ def show_image(obj):
img_url = "%s%s" % (UPLOADS_URL, obj.image) img_url = "%s%s" % (UPLOADS_URL, obj.image)
return ('<a href="%s" target="_new"><img src="%s" width="48" height="48" /></a>' % return ('<a href="%s" target="_new"><img src="%s" width="48" height="48" /></a>' %
(img_url, img_url)) (img_url, img_url))
show_image.allow_tags = True show_image.allow_tags = True
show_image.short_description = "Image" 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 ('<a href="%s">%s %s</a> (<a href="%s">new</a>)' %
(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', ) filter_horizontal = ('prerequisites', )
prepopulated_fields = {"slug": ("title",)}
formfield_overrides = { formfield_overrides = {
models.ManyToManyField: { models.ManyToManyField: {
"widget": forms.widgets.SelectMultiple(attrs={"size": 25}) "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 '<a href="%s">%s</a>' % (url, self.badge)
badge_link.allow_tags = True
badge_link.short_description = 'Badge'
class AwardAdmin(admin.ModelAdmin): class AwardAdmin(admin.ModelAdmin):
list_display = (show_unicode, badge_link, show_image, 'user', 'creator',
list_display = (show_unicode, 'badge', 'user', 'creator', show_image, 'created', ) 'created', )
fields = ('badge', 'user', 'creator', ) fields = ('badge', 'user', 'creator', )
search_fields = ("badge__title", "badge__slug", "badge__description",)
class ProgressAdmin(admin.ModelAdmin): class ProgressAdmin(admin.ModelAdmin):
pass pass
admin.site.register(Badge, BadgerAdmin) def claim_code_link(self):
admin.site.register(Award, AwardAdmin) return '<a href="%s">%s</a>' % (self.get_claim_url(), self.claim_code)
admin.site.register(Progress, ProgressAdmin)
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)

Просмотреть файл

@ -5,6 +5,7 @@ from django.conf import settings
from django.contrib.auth.models import SiteProfileNotAvailable from django.contrib.auth.models import SiteProfileNotAvailable
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from django.utils.html import conditional_escape
try: try:
from commons.urlresolvers import reverse from commons.urlresolvers import reverse
@ -61,3 +62,13 @@ def user_awards(user):
@register.function @register.function
def user_badges(user): def user_badges(user):
return Badge.objects.filter(creator=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"""<img class="qrcode" src="%s" width="%s" height="%s" alt="%s" />""" %
(url, size, size, alt))

Просмотреть файл

@ -267,21 +267,31 @@ class Badge(models.Model):
"""Representation of a badge""" """Representation of a badge"""
objects = BadgeManager() objects = BadgeManager()
title = models.CharField(max_length=255, blank=False, unique=True) title = models.CharField(max_length=255, blank=False, unique=True,
slug = models.SlugField(blank=False, unique=True) help_text="Short, descriptive title")
description = models.TextField(blank=True) 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, image = models.ImageField(blank=True, null=True,
storage=BADGE_UPLOADS_FS, storage=BADGE_UPLOADS_FS, upload_to=mk_upload_to('image','png'),
upload_to=mk_upload_to('image','png')) help_text="Upload an image to represent the badge")
prerequisites = models.ManyToManyField('self', symmetrical=False, prerequisites = models.ManyToManyField('self', symmetrical=False,
blank=True, null=True) blank=True, null=True,
unique = models.BooleanField(default=False) 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) creator = models.ForeignKey(User, blank=True, null=True)
created = models.DateTimeField(auto_now_add=True, blank=False) created = models.DateTimeField(auto_now_add=True, blank=False)
modified = models.DateTimeField(auto_now=True, blank=False) modified = models.DateTimeField(auto_now=True, blank=False)
class Meta: class Meta:
unique_together = ('title', 'slug') unique_together = ('title', 'slug')
ordering = ['-modified', '-created']
get_permissions_for = get_permissions_for get_permissions_for = get_permissions_for
@ -457,6 +467,9 @@ class Award(models.Model):
get_permissions_for = get_permissions_for get_permissions_for = get_permissions_for
class Meta:
ordering = ['-modified', '-created']
def __unicode__(self): def __unicode__(self):
by = self.creator and (' by %s' % self.creator) or '' by = self.creator and (' by %s' % self.creator) or ''
return u'Award of %s to %s%s' % (self.badge, self.user, by) 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) reusable = models.BooleanField(default=False)
email = models.EmailField(blank=True, null=True, db_index=True) email = models.EmailField(blank=True, null=True, db_index=True)
claim_code = models.CharField(max_length=CLAIM_CODE_LENGTH, claim_code = models.CharField(max_length=CLAIM_CODE_LENGTH,
default=make_random_code, editable=False, unique=True, default=make_random_code, unique=True, db_index=True)
db_index=True)
creator = models.ForeignKey(User, blank=True, null=True) creator = models.ForeignKey(User, blank=True, null=True)
created = models.DateTimeField(auto_now_add=True, blank=False) created = models.DateTimeField(auto_now_add=True, blank=False)
modified = models.DateTimeField(auto_now=True, blank=False) modified = models.DateTimeField(auto_now=True, blank=False)
class Meta:
ordering = ['-modified', '-created']
def get_claim_url(self): def get_claim_url(self):
"""Get the URL to a page where this DeferredAward can be claimed.""" """Get the URL to a page where this DeferredAward can be claimed."""
return reverse('badger.views.claim_deferred_award', return reverse('badger.views.claim_deferred_award',

Просмотреть файл

@ -20,6 +20,8 @@ urlpatterns = patterns('badger.views',
name='badger.award_detail'), name='badger.award_detail'),
url(r'^claim/(?P<claim_code>[^/]+)/?$', 'claim_deferred_award', url(r'^claim/(?P<claim_code>[^/]+)/?$', 'claim_deferred_award',
name='badger.claim_deferred_award'), name='badger.claim_deferred_award'),
url(r'^claim/?$', 'claim_deferred_award',
name='badger.claim_deferred_award_form'),
url(r'^badge/(?P<slug>[^/]+)/award', 'award_badge', url(r'^badge/(?P<slug>[^/]+)/award', 'award_badge',
name='badger.award_badge'), name='badger.award_badge'),
url(r'^badge/(?P<slug>[^\.]+)\.json$', 'detail', url(r'^badge/(?P<slug>[^\.]+)\.json$', 'detail',

Просмотреть файл

@ -163,8 +163,10 @@ def award_detail(request, slug, id, format="html"):
@require_http_methods(['GET', 'POST']) @require_http_methods(['GET', 'POST'])
@login_required @login_required
def claim_deferred_award(request, claim_code): def claim_deferred_award(request, claim_code=None):
"""Deferred award detail view""" """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) deferred_award = get_object_or_404(DeferredAward, claim_code=claim_code)
if request.method == "POST": if request.method == "POST":