Implement badges

This commit is contained in:
Trever Fischer
2012-06-07 15:47:03 -04:00
parent fdd9438966
commit 8bf1478208
19 changed files with 2721 additions and 3 deletions

0
badges/__init__.py Normal file
View File

4
badges/admin.py Normal file
View File

@@ -0,0 +1,4 @@
import models
from django.contrib import admin
admin.site.register(models.Badge)

9
badges/api.py Normal file
View File

@@ -0,0 +1,9 @@
import models
import django.dispatch
badge_awarded = django.dispatch.Signal(providing_args=["user",])
def award(user, badge):
user.badges += badge
user.save()
badge_awarded.send_robust(sender=badge, user=user)

43
badges/models.py Normal file
View File

@@ -0,0 +1,43 @@
from django.db import models
from django.contrib.auth.models import User
def unique_slug(item,slug_source,slug_field):
"""Ensures a unique slug field by appending an integer counter to duplicate slugs.
The item's slug field is first prepopulated by slugify-ing the source field. If that value already exists, a counter is appended to the slug, and the counter incremented upward until the value is unique.
For instance, if you save an object titled Daily Roundup, and the slug daily-roundup is already taken, this function will try daily-roundup-2, daily-roundup-3, daily-roundup-4, etc, until a unique value is found.
Call from within a model's custom save() method like so:
unique_slug(item, slug_source='field1', slug_field='field2')
where the value of field slug_source will be used to prepopulate the value of slug_field.
"""
if not getattr(item, slug_field): # if it's already got a slug, do nothing.
from django.template.defaultfilters import slugify
slug = slugify(getattr(item,slug_source))
itemModel = item.__class__
# the following gets all existing slug values
allSlugs = [sl.values()[0] for sl in itemModel.objects.values(slug_field)]
if slug in allSlugs:
import re
counterFinder = re.compile(r'-\d+$')
counter = 2
slug = "%s-%i" % (slug, counter)
while slug in allSlugs:
slug = re.sub(counterFinder,"-%i" % counter, slug)
counter += 1
setattr(item,slug_field,slug)
class Badge(models.Model):
name = models.TextField()
description = models.TextField()
slug = models.SlugField(blank=True)
users = models.ManyToManyField(User, related_name='badges')
secret = models.BooleanField(default=False)
def save(self, *args, **kwargs):
unique_slug(self, slug_source='name', slug_field='slug')
super(Badge, self).save(*args, **kwargs)
def __unicode__(self):
return self.name

16
badges/tests.py Normal file
View File

@@ -0,0 +1,16 @@
"""
This file demonstrates writing tests using the unittest module. These will pass
when you run "manage.py test".
Replace this with more appropriate tests for your application.
"""
from django.test import TestCase
class SimpleTest(TestCase):
def test_basic_addition(self):
"""
Tests that 1 + 1 always equals 2.
"""
self.assertEqual(1 + 1, 2)

5
badges/urls.py Normal file
View File

@@ -0,0 +1,5 @@
from django.conf.urls.defaults import patterns, include, url
urlpatterns = patterns('badges',
url(r'^$', 'views.index'),
)

6
badges/views.py Normal file
View File

@@ -0,0 +1,6 @@
import models
from django.shortcuts import render_to_response
def index(request):
badges = models.Badge.objects.filter(secret=False)
return render_to_response('badges/index.html', {'badges': badges})

View File

@@ -135,7 +135,8 @@ INSTALLED_APPS = (
'messages',
'notification',
'piston',
'donate'
'donate',
'badges',
# Uncomment the next line to enable admin documentation:
# 'django.contrib.admindocs',
)

41
static/css/badges.css Normal file
View File

@@ -0,0 +1,41 @@
.badge {
width: 32px;
height: 32px;
background: url("badges/background.png");
}
.badge-list {
text-align: center;
padding: 0;
margin-left: 0;
text-indent: 0;
list-style-type: none;
}
.no-badge-message {
color: #444;
margin: 0 !important;
background: transparent !important;
text-align: center;
display:block;
width: 100%;
}
.badge-holder {
width: 32px;
height: 32px;
display:inline;
margin: 0px !important;
}
.badge-description {
display:none;
}
.badge:hover .badge-description {
display:block;
background-color:#000;
border:1px solid #f80;
position: absolute;
padding: 3px;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 39 KiB

1276
static/css/badges/star.svg Normal file

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 39 KiB

View File

@@ -1,5 +1,6 @@
@font-face {font-family:"Minecraft";src:url("minecraft.eot") format("eot"),url("minecraft.woff") format("woff"),url("minecraft.ttf") format("truetype"),url("minecraft.svg#Minecraft") format("svg");font-weight:normal;font-style:normal;}
html {
background: url(img/blocks.gif);
}
@@ -144,16 +145,27 @@ tbody tr:nth-child(odd) {
.user-tile {
display: block;
width: 150px;
height: 150px;
min-height: 150px;
text-align: center;
}
.user-tile .badges {
padding: 0;
margin: 0;
text-align: center;
background-color:#000;
}
.user-tile .badge-holder {
background:#000;
}
.post .user-tile {
float: left;
}
.user-tile .byline {
padding: 1em;
padding: 10px;
}
.user-tile a {

View File

@@ -5,5 +5,9 @@
<div class="avatar">{%avatar user%}</div>
{{user}}
</a>
<br style="clear:both"/>
<div class="badges">
{%include "common/badge_list.html" with badges=user.badges.all %}
</div>
</div>
</div>

View File

@@ -0,0 +1,11 @@
{% extends "base_simple.html" %}
{% block title %}Badges{% endblock %}
{% block content %}
<ul>
{% for badge in badges %}
<li class="badge-holder"><div class="badge badge-{{badge.slug}}"><div class="badge-description">{{badge.name}}</div></div></li>
{% endfor %}
</ul>
{% endblock %}

View File

@@ -12,6 +12,7 @@
<link rel="stylesheet" type="text/css" href="{{ STATIC_PREFIX }}/css/960.css"/>
<link rel="stylesheet" type="text/css" href="{{ STATIC_PREFIX }}/css/jquery-ui.css"/>
<link rel="stylesheet" type="text/css" href="{{ STATIC_PREFIX }}/css/main.css?{{app_version}}"/>
<link rel="stylesheet" type="text/css" href="{{ STATIC_PREFIX }}/css/badges.css?{{app_version}}"/>
<link rel="icon" type="image/png" href="{{ STATIC_PREFIX }}/images/logo.png"/>
{% block extracss %}{% endblock extracss %}
<!-- Google+ -->

View File

@@ -0,0 +1,12 @@
<ul class="badge-list">
{% if badges %}
{% for badge in badges %}
<li class="badge-holder"><div class="badge badge-{{badge.slug}}"><div class="badge-description">{{badge.name}}</div></div></li>
{% endfor %}
{% else %}
<li class="no-badge-message">
no badges
</li>
{% endif %}
</ul>
<br style="clear:both"/>

View File

@@ -23,6 +23,7 @@ urlpatterns = patterns('',
url(r'^i/(?P<code>.+)', 'local.views.claimInvite'),
url(r'^donate/', include('donate.urls')),
url(r'^f/(?P<id>.*)', 'forums.views.post'),
url(r'^badges/', include('badges.urls')),
)
urlpatterns += staticfiles_urlpatterns()