Implement badges
This commit is contained in:
0
badges/__init__.py
Normal file
0
badges/__init__.py
Normal file
4
badges/admin.py
Normal file
4
badges/admin.py
Normal file
@@ -0,0 +1,4 @@
|
||||
import models
|
||||
from django.contrib import admin
|
||||
|
||||
admin.site.register(models.Badge)
|
9
badges/api.py
Normal file
9
badges/api.py
Normal 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
43
badges/models.py
Normal 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
16
badges/tests.py
Normal 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
5
badges/urls.py
Normal 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
6
badges/views.py
Normal 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})
|
@@ -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
41
static/css/badges.css
Normal 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;
|
||||
}
|
BIN
static/css/badges/background.png
Normal file
BIN
static/css/badges/background.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
BIN
static/css/badges/caminus.png
Normal file
BIN
static/css/badges/caminus.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.8 KiB |
1276
static/css/badges/caminus.svg
Normal file
1276
static/css/badges/caminus.svg
Normal file
File diff suppressed because it is too large
Load Diff
After Width: | Height: | Size: 39 KiB |
1276
static/css/badges/star.svg
Normal file
1276
static/css/badges/star.svg
Normal file
File diff suppressed because it is too large
Load Diff
After Width: | Height: | Size: 39 KiB |
@@ -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 {
|
||||
|
@@ -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>
|
||||
|
11
templates/badges/index.html
Normal file
11
templates/badges/index.html
Normal 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 %}
|
@@ -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+ -->
|
||||
|
12
templates/common/badge_list.html
Normal file
12
templates/common/badge_list.html
Normal 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"/>
|
Reference in New Issue
Block a user