import re from string import lower from django.core.exceptions import MultipleObjectsReturned from django.db.models.signals import post_save from forum.models import Badge, Node, Action from forum.actions import AwardAction import logging installed = dict([(b.cls, b) for b in Badge.objects.all()]) class BadgesMeta(type): by_class = {} by_id = {} def __new__(mcs, name, bases, dic): badge = type.__new__(mcs, name, bases, dic) if not dic.get('abstract', False): if not name in installed: ondb = Badge(cls=name, type=dic.get('type', Badge.BRONZE)) ondb.save() else: ondb = installed[name] badge.ondb = ondb.id inst = badge() def hook(action, new): user = inst.award_to(action) if user: badge.award(user, action, badge.award_once) for action in badge.listen_to: action.hook(hook) BadgesMeta.by_class[name] = inst BadgesMeta.by_id[ondb.id] = inst return badge class AbstractBadge(object): __metaclass__ = BadgesMeta abstract = True award_once = False @property def name(self): raise NotImplementedError @property def description(self): raise NotImplementedError @classmethod def award(cls, user, action, once=False): db_object = Badge.objects.get(id=cls.ondb) try: if once: node = None awarded = AwardAction.get_for(user, db_object) else: node = action.node awarded = AwardAction.get_for(user, db_object, node) trigger = isinstance(action, Action) and action or None if not awarded: AwardAction(user=user, node=node).save(data=dict(badge=db_object, trigger=trigger)) except MultipleObjectsReturned: if node: logging.error('Found multiple %s badges awarded for user %s (%s)' % (self.name, user.username, user.id)) else: logging.error('Found multiple %s badges awarded for user %s (%s) and node %s' % (self.name, user.username, user.id, node.id))