forked from mitsuhiko/solace
-
Notifications
You must be signed in to change notification settings - Fork 1
/
badges.py
217 lines (178 loc) · 7.14 KB
/
badges.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
# -*- coding: utf-8 -*-
"""
solace.badges
~~~~~~~~~~~~~
This module implements the badge system.
:copyright: (c) 2010 by the Solace Team, see AUTHORS for more details.
:license: BSD, see LICENSE for more details.
"""
from operator import attrgetter
from solace.i18n import lazy_gettext, _
from solace.utils.remoting import RemoteObject
def try_award(event, *args):
"""Tries to avard a badge for the given event. The events correspond
to the `on_X` callbacks on the badges, just without the `on_` prefix.
"""
lookup = attrgetter('on_' + event)
for badge in badge_list:
cb = lookup(badge)
if cb is None:
continue
user = cb(*args)
if user is not None:
if isinstance(user, tuple):
user, payload = user
else:
payload = None
if badge.single_awarded and badge in user.badges:
continue
user._badges.append(UserBadge(badge, payload))
# inactive or banned users don't get messages.
if user.is_active and not user.is_banned:
UserMessage(user, _(u'You earned the “%s” badge') % badge.name)
_numeric_levels = dict(zip(('bronce', 'silver', 'gold', 'platin'),
range(4)))
class Badge(RemoteObject):
"""Represents a badge.
It can react to the following events::
on_vote = lambda user, post, delta
on_accept = lambda user, post, answer
on_reply = lambda user, post
on_new_topic = lambda user, topic
on_edit = lambda user, post
"""
remote_object_type = 'solace.badge'
public_fields = ('level', 'identifier', 'name', 'description')
def __init__(self, level, identifier, name, description=None,
single_awarded=False,
on_vote=None, on_accept=None, on_reply=None,
on_new_topic=None, on_edit=None):
assert level in ('bronce', 'silver', 'gold', 'platin')
assert len(identifier) <= 30
self.level = level
self.identifier = identifier
self.name = name
self.single_awarded = single_awarded
self.description = description
self.on_vote = on_vote
self.on_accept = on_accept
self.on_reply = on_reply
self.on_new_topic = on_new_topic
self.on_edit = on_edit
@property
def numeric_level(self):
return _numeric_levels[self.level]
def get_url_values(self):
return 'badges.show_badge', {'identifier': self.identifier}
def __repr__(self):
return '<%s \'%s\' (%s)>' % (
type(self).__name__,
self.name.encode('utf-8'),
('bronce', 'silver', 'gold', 'platin')[self.numeric_level]
)
def _try_award_special_answer(post, badge, votes_required):
"""Helper for nice and good answer."""
pid = str(post.id)
user = post.author
for user_badge in user._badges:
if user_badge.badge == badge and \
user_badge.payload == pid:
return
if post.is_answer and post.votes >= votes_required:
return user, pid
def _try_award_self_learner(post):
"""Helper for the self learner badge."""
pid = str(post.id)
user = post.author
for user_badge in user._badges:
if user_badge.badge == SELF_LEARNER and \
user_badge.payload == pid:
return
if post.is_answer and post.author == post.topic.author \
and post.votes >= 3:
return user, pid
def _try_award_reversal(post):
"""Helper for the reversal badge."""
pid = str(post.id)
user = post.author
for user_badge in user._badges:
if user_badge.badge == REVERSAL and \
user_badge.payload == pid:
return
if post.is_answer and post.votes >= 20 and \
post.topic.votes <= -5:
return user, pid
CRITIC = Badge('bronce', 'critic', lazy_gettext(u'Critic'),
lazy_gettext(u'First down vote'),
single_awarded=True,
on_vote=lambda user, post, delta:
user if delta < 0 and user != post.author else None
)
SELF_CRITIC = Badge('silver', 'self-critic', lazy_gettext(u'Self-Critic'),
lazy_gettext(u'First downvote on own reply or question'),
single_awarded=True,
on_vote=lambda user, post, delta:
user if delta < 0 and user == post.author else None
)
EDITOR = Badge('bronce', 'editor', lazy_gettext(u'Editor'),
lazy_gettext(u'First edited post'),
single_awarded=True,
on_edit=lambda user, post: user
)
INQUIRER = Badge('bronce', 'inquirer', lazy_gettext(u'Inquirer'),
lazy_gettext(u'First asked question'),
single_awarded=True,
on_new_topic=lambda user, topic: user
)
TROUBLESHOOTER = Badge('silver', 'troubleshooter',
lazy_gettext(u'Troubleshooter'),
lazy_gettext(u'First answered question'),
single_awarded=True,
on_accept=lambda user, topic, post: post.author if post else None
)
NICE_ANSWER = Badge('bronce', 'nice-answer', lazy_gettext(u'Nice Answer'),
lazy_gettext(u'Answer was upvoted 10 times'),
on_accept=lambda user, topic, post: _try_award_special_answer(post,
NICE_ANSWER, 10) if post else None,
on_vote=lambda user, post, delta: _try_award_special_answer(post,
NICE_ANSWER, 10)
)
GOOD_ANSWER = Badge('silver', 'good-answer', lazy_gettext(u'Good Answer'),
lazy_gettext(u'Answer was upvoted 25 times'),
on_accept=lambda user, topic, post: _try_award_special_answer(post,
GOOD_ANSWER, 25) if post else None,
on_vote=lambda user, post, delta: _try_award_special_answer(post,
GOOD_ANSWER, 25)
)
GREAT_ANSWER = Badge('gold', 'great-answer', lazy_gettext(u'Great Answer'),
lazy_gettext(u'Answer was upvoted 75 times'),
on_accept=lambda user, topic, post: _try_award_special_answer(post,
GOOD_ANSWER, 75) if post else None,
on_vote=lambda user, post, delta: _try_award_special_answer(post,
GOOD_ANSWER, 75)
)
UNIQUE_ANSWER = Badge('platin', 'unique-answer', lazy_gettext(u'Unique Answer'),
lazy_gettext(u'Answer was upvoted 150 times'),
on_accept=lambda user, topic, post: _try_award_special_answer(post,
GOOD_ANSWER, 150) if post else None,
on_vote=lambda user, post, delta: _try_award_special_answer(post,
GOOD_ANSWER, 150)
)
REVERSAL = Badge('gold', 'reversal', lazy_gettext(u'Reversal'),
lazy_gettext(u'Provided answer of +20 score to a question of -5 score'),
on_accept=lambda user, topic, post: _try_award_reversal(post) if post else None,
on_vote=lambda user, post, delta: _try_award_reversal(post)
)
SELF_LEARNER = Badge('silver', 'self-learner', lazy_gettext(u'Self-Learner'),
lazy_gettext(u'Answered your own question with at least 4 upvotes'),
on_accept=lambda user, topic, post: _try_award_self_learner(post) if post else None,
on_vote=lambda user, post, delta: _try_award_self_learner(post)
)
#: list of all badges
badge_list = [CRITIC, EDITOR, INQUIRER, TROUBLESHOOTER, NICE_ANSWER,
GOOD_ANSWER, SELF_LEARNER, SELF_CRITIC, GREAT_ANSWER,
UNIQUE_ANSWER, REVERSAL]
#: all the badges by key
badges_by_id = dict((x.identifier, x) for x in badge_list)
# circular dependencies
from solace.models import UserBadge, UserMessage