2 from string import lower
4 from django.contrib.contenttypes.models import ContentType
5 from django.db.models.signals import post_save
7 from forum.models.user import activity_record
8 from forum.models.base import denorm_update
9 from forum.models import Badge, Award, Activity, Node
13 class AbstractBadge(object):
19 return " ".join(re.findall(r'([A-Z][a-z1-9]+)', re.sub('Badge', '', self.__class__.__name__)))
22 def description(self):
23 raise NotImplementedError
25 def __new__(cls, *args, **kwargs):
26 if cls._instance is None:
27 cls.badge = "-".join(map(lower, re.findall(r'([A-Z][a-z1-9]+)', re.sub('Badge', '', cls.__name__))))
28 cls._instance = super(AbstractBadge, cls).__new__(cls, *args, **kwargs)
34 installed = Badge.objects.get(slug=self.badge)
36 badge = Badge(name=self.name, description=self.description, slug=self.badge, type=self.type)
39 def award_badge(self, user, obj=None, award_once=False):
41 badge = Badge.objects.get(slug=self.badge)
43 logging.log(1, 'Trying to award a badge not installed in the database.')
46 content_type = ContentType.objects.get_for_model(obj.__class__)
48 awarded = user.awards.filter(badge=badge)
51 awarded = awarded.filter(content_type=content_type, object_id=obj.id)
54 logging.log(1, 'Trying to award badged already awarded.')
57 award = Award(user=user, badge=badge, content_type=content_type, object_id=obj.id)
60 class CountableAbstractBadge(AbstractBadge):
62 def __init__(self, model, field, expected_value, handler):
63 def wrapper(instance, sfield, old, new, **kwargs):
64 if sfield == field and (new == expected_value) or (old < expected_value and new > expected_value):
65 handler(instance=instance)
67 denorm_update.connect(wrapper, sender=model, weak=False)
69 class PostCountableAbstractBadge(CountableAbstractBadge):
70 def __init__(self, model, field, expected_value):
72 def handler(instance):
73 self.award_badge(instance.author, instance)
75 super(PostCountableAbstractBadge, self).__init__(model, field, expected_value, handler)
77 class NodeCountableAbstractBadge(CountableAbstractBadge):
78 def __init__(self, node_type, field, expected_value):
80 def handler(instance):
81 if instance.node_type == node_type:
82 self.award_badge(instance.author, instance)
84 super(NodeCountableAbstractBadge, self).__init__(Node, field, expected_value, handler)
86 class ActivityAbstractBadge(AbstractBadge):
88 def __init__(self, activity_type, handler):
90 def wrapper(sender, **kwargs):
91 handler(instance=kwargs['instance'])
93 activity_record.connect(wrapper, sender=activity_type, weak=False)
96 class ActivityCountAbstractBadge(AbstractBadge):
98 def __init__(self, activity_type, count):
100 def handler(sender, **kwargs):
101 instance = kwargs['instance']
102 if Activity.objects.filter(user=instance.user, activity_type__in=activity_type).count() == count:
103 self.award_badge(instance.user, instance.content_object)
105 if not isinstance(activity_type, (tuple, list)):
106 activity_type = (activity_type, )
108 for type in activity_type:
109 activity_record.connect(handler, sender=type, weak=False)
111 class FirstActivityAbstractBadge(ActivityCountAbstractBadge):
113 def __init__(self, activity_type):
114 super(FirstActivityAbstractBadge, self).__init__(activity_type, 1)