Using brabeion
¶
Getting Started¶
-
class
brabeion.base.
Badge
¶
brabeion
works by allowing you to define your badges as subclasses of a
common Badge
class and registering them with brabeion
. For example if
your site gave users points, and you wanted to award three ranks of badges
based on how many points a user had your badge might look like this:
from brabeion import badges
from brabeion.base import Badge, BadgeAwarded
class PointsBadge(Badge):
slug = "points"
levels = [
"Bronze",
"Silver",
"Gold",
]
events = [
"points_awarded",
]
multiple = False
def award(self, **state):
user = state["user"]
points = user.get_profile().points
if points > 10000:
return BadgeAwarded(level=3)
elif points > 7500:
return BadgeAwarded(level=2)
elif points > 5000:
return BadgeAwarded(level=1)
badges.register(PointsBadge)
There are a few relevant attributes and methods here.
-
slug
¶ The unique identifier for this
Badge
, it should never change.
-
levels
¶ A list of the levels available for this badge (if this badge doesn’t have levels it should just be a list with one item). It can either be a list of strings, which are the names of the levels, or a list of
brabeion.base.BadgeDetail
which have both names and description.
-
events
¶ A list of events that can possibly trigger this badge to be awarded. How events are triggered is described in further detail below.
-
multiple
¶ A boolean specifying whether or not this badge can be awarded to the same user multiple times, currently if this badge has multiple levels this must be
False
.
-
award
(self, **state)¶ This method returns whether or not a user should be awarded this badge.
state
is guarnteed to have a"user"
key, as well as any other custom data you provide. It should return either aBadgeAwarded
instance, orNone
. If thisBadge
doesn’t have multiple levelsBadgeAwarded
doesn’t need to be provided an explicit level.Note
BadgeAwarded.level
is 1-indexed.
Now that you have your PointsBadge
class you need to be able to tell
brabeion
when to try to give it to a user. To do this, any time a user
might have received a badge just call badges.possibly_award_badge
with
the name of the event, and whatever state these events might need and
brabeion
will handle the details of seeing what badges need to be awarded
to the user:
from brabeion import badges
def my_view(request):
if request.method == "POST":
# do some things
request.user.profile.award_points(15)
badges.possibly_award_badge("points_awarded", user=request.user)
# more view
By default badges will be awarded at the current time, if you need to overide
the award time of the badge you can pass a force_timestamp
keyword argument
to possibly_award_badge()
.
Asynchronous Badges¶
Note
To use asynchronous badges you must have celery installed and configured.
If your Badge.award()
method takes a long time to compute it may be
prohibitively expensive to call during the request/response cycle. To solve
this problem brabeion
provides an async
option to Badges
. If this
is True
brabeion
will defer calling your award()
method, using
celery
, and it will be called at a later time, by another process (read the
celery docs for more information on how
celery
works).
Because award()
won’t be called until later you can define a freeze()
method which allows you to provide and additional state that you’ll need to
compute award()
correctly. This may be necessary because your Badge
requires some mutable state.
class AddictBadge(Badge):
# stuff
async = True
def freeze(self, **state):
state["current_day"] = datetime.today()
return state
In this example badge the user will be awarded the AddictBadge
when they’ve
visited the site every day for a month, this is expensive to calculate so it
will be done outside the request/response cycle. However, what happens if they
visit the site right before midnight, and then the award()
method isn’t
actually called until the next day? Using the freeze method you can provide
additional state to the award()
method.