X-Git-Url: https://git.openstreetmap.org./osqa.git/blobdiff_plain/854b33110cde1c2bf725f47d9809c23d64577af9..ff9cf41fc82917f82e95d99eae1dd0077c1fa32b:/forum/badges/base.py diff --git a/forum/badges/base.py b/forum/badges/base.py index c2dbe30..79fcb52 100644 --- a/forum/badges/base.py +++ b/forum/badges/base.py @@ -1,114 +1,77 @@ import re from string import lower -from django.contrib.contenttypes.models import ContentType +from django.core.exceptions import MultipleObjectsReturned from django.db.models.signals import post_save -from forum.models.user import activity_record -from forum.models import Badge, Award, Activity, Node +from forum.models import Badge, Node, Action +from forum.actions import AwardAction import logging -class AbstractBadge(object): - - _instance = None - - @property - def name(self): - return " ".join(re.findall(r'([A-Z][a-z1-9]+)', re.sub('Badge', '', self.__class__.__name__))) - - @property - def description(self): - raise NotImplementedError - - def __new__(cls, *args, **kwargs): - if cls._instance is None: - cls.badge = "-".join(map(lower, re.findall(r'([A-Z][a-z1-9]+)', re.sub('Badge', '', cls.__name__)))) - cls._instance = super(AbstractBadge, cls).__new__(cls, *args, **kwargs) - - return cls._instance - - def install(self): - try: - installed = Badge.objects.get(slug=self.badge) - except: - badge = Badge(name=self.name, description=self.description, slug=self.badge, type=self.type) - badge.save() - - def award_badge(self, user, obj=None, award_once=False): - try: - badge = Badge.objects.get(slug=self.badge) - except: - logging.log('Trying to award a badge not installed in the database.') - return - - content_type = ContentType.objects.get_for_model(obj.__class__) - - awarded = user.awards.filter(badge=badge) +installed = dict([(b.cls, b) for b in Badge.objects.all()]) - if not award_once: - awarded = awarded.filter(content_type=content_type, object_id=obj.id) +class BadgesMeta(type): + by_class = {} + by_id = {} - if len(awarded): - logging.log(1, 'Trying to award badged already awarded.') - return - - award = Award(user=user, badge=badge, content_type=content_type, object_id=obj.id) - award.save() + def __new__(mcs, name, bases, dic): + badge = type.__new__(mcs, name, bases, dic) -class CountableAbstractBadge(AbstractBadge): + if not dic.get('abstract', False): + if not name in installed: + badge.ondb = Badge(cls=name, type=dic.get('type', Badge.BRONZE)) + badge.ondb.save() + else: + badge.ondb = installed[name] - def __init__(self, model, field, expected_value, handler): - def wrapper(instance, **kwargs): - dirty_fields = instance.get_dirty_fields() - if field in dirty_fields and instance.__dict__[field] == expected_value: - handler(instance=instance) - - post_save.connect(wrapper, sender=model, weak=False) + inst = badge() -class PostCountableAbstractBadge(CountableAbstractBadge): - def __init__(self, model, field, expected_value): + def hook(action, new): + user = inst.award_to(action) - def handler(instance): - self.award_badge(instance.author, instance) + if user: + badge.award(user, action, badge.award_once) - super(PostCountableAbstractBadge, self).__init__(model, field, expected_value, handler) + for action in badge.listen_to: + action.hook(hook) -class NodeCountableAbstractBadge(CountableAbstractBadge): - def __init__(self, node_type, field, expected_value): + BadgesMeta.by_class[name] = badge + badge.ondb.__dict__['_class'] = inst + BadgesMeta.by_id[badge.ondb.id] = badge - def handler(instance): - if instance.node_type == node_type: - self.award_badge(instance.author, instance) + return badge - super(NodeCountableAbstractBadge, self).__init__(Node, field, expected_value, handler) - -class ActivityAbstractBadge(AbstractBadge): - - def __init__(self, activity_type, handler): - - def wrapper(sender, **kwargs): - handler(instance=kwargs['instance']) - - activity_record.connect(wrapper, sender=activity_type, weak=False) - - -class ActivityCountAbstractBadge(AbstractBadge): - - def __init__(self, activity_type, count): - - def handler(sender, **kwargs): - instance = kwargs['instance'] - if Activity.objects.filter(user=instance.user, activity_type__in=activity_type).count() == count: - self.award_badge(instance.user, instance.content_object) +class AbstractBadge(object): + __metaclass__ = BadgesMeta - if not isinstance(activity_type, (tuple, list)): - activity_type = (activity_type, ) + abstract = True + award_once = False - for type in activity_type: - activity_record.connect(handler, sender=type, weak=False) + @property + def name(self): + raise NotImplementedError -class FirstActivityAbstractBadge(ActivityCountAbstractBadge): + @property + def description(self): + raise NotImplementedError - def __init__(self, activity_type): - super(FirstActivityAbstractBadge, self).__init__(activity_type, 1) + @classmethod + def award(cls, user, action, once=False): + try: + if once: + node = None + awarded = AwardAction.get_for(user, cls.ondb) + else: + node = action.node + awarded = AwardAction.get_for(user, cls.ondb, node) + + trigger = isinstance(action, Action) and action or None + + if not awarded: + AwardAction(user=user, node=node).save(data=dict(badge=cls.ondb, 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)) \ No newline at end of file