]> git.openstreetmap.org Git - osqa.git/blob - forum_modules/default_badges/badges.py
more improvements in cache and denormalized data handling
[osqa.git] / forum_modules / default_badges / badges.py
1 from datetime import timedelta
2
3 from django.db.models.signals import post_save
4 from django.utils.translation import ugettext as _
5
6 from forum.badges.base import PostCountableAbstractBadge, ActivityAbstractBadge, FirstActivityAbstractBadge, \
7         ActivityCountAbstractBadge, CountableAbstractBadge, AbstractBadge, NodeCountableAbstractBadge
8 from forum.models import Node, Question, Answer, Activity, Tag
9 from forum.models.user import activity_record
10 from forum.models.base import denorm_update
11 from forum import const
12
13 import settings
14
15 class PopularQuestionBadge(PostCountableAbstractBadge):
16     type = const.BRONZE_BADGE
17     description = _('Asked a question with %s views') % str(settings.POPULAR_QUESTION_VIEWS)
18
19     def __init__(self):
20         super(PopularQuestionBadge, self).__init__(Question, 'view_count', settings.POPULAR_QUESTION_VIEWS)
21
22 class NotableQuestionBadge(PostCountableAbstractBadge):
23     type = const.SILVER_BADGE
24     description = _('Asked a question with %s views') % str(settings.NOTABLE_QUESTION_VIEWS)
25
26     def __init__(self):
27         super(NotableQuestionBadge, self).__init__(Question, 'view_count', settings.NOTABLE_QUESTION_VIEWS)
28
29 class FamousQuestionBadge(PostCountableAbstractBadge):
30     type = const.GOLD_BADGE
31     description = _('Asked a question with %s views') % str(settings.FAMOUS_QUESTION_VIEWS)
32
33     def __init__(self):
34         super(FamousQuestionBadge, self).__init__(Question, 'view_count', settings.FAMOUS_QUESTION_VIEWS)
35
36
37 class NiceAnswerBadge(NodeCountableAbstractBadge):
38     type = const.BRONZE_BADGE
39     description = _('Answer voted up %s times') % str(settings.NICE_ANSWER_VOTES_UP)
40
41     def __init__(self):
42         super(NiceAnswerBadge, self).__init__("answer", 'vote_up_count', settings.NICE_ANSWER_VOTES_UP)
43
44 class NiceQuestionBadge(NodeCountableAbstractBadge):
45     type = const.BRONZE_BADGE
46     description = _('Question voted up %s times') % str(settings.NICE_QUESTION_VOTES_UP)
47
48     def __init__(self):
49         super(NiceQuestionBadge, self).__init__("question", 'vote_up_count', settings.NICE_QUESTION_VOTES_UP)
50
51 class GoodAnswerBadge(NodeCountableAbstractBadge):
52     type = const.SILVER_BADGE
53     description = _('Answer voted up %s times') % str(settings.GOOD_ANSWER_VOTES_UP)
54
55     def __init__(self):
56         super(GoodAnswerBadge, self).__init__("answer", 'vote_up_count', settings.GOOD_ANSWER_VOTES_UP)
57
58 class GoodQuestionBadge(NodeCountableAbstractBadge):
59     type = const.SILVER_BADGE
60     description = _('Question voted up %s times') % str(settings.GOOD_QUESTION_VOTES_UP)
61
62     def __init__(self):
63         super(GoodQuestionBadge, self).__init__("question", 'vote_up_count', settings.GOOD_QUESTION_VOTES_UP)
64
65 class GreatAnswerBadge(NodeCountableAbstractBadge):
66     type = const.GOLD_BADGE
67     description = _('Answer voted up %s times') % str(settings.GREAT_ANSWER_VOTES_UP)
68
69     def __init__(self):
70         super(GreatAnswerBadge, self).__init__("answer", 'vote_up_count', settings.GREAT_ANSWER_VOTES_UP)
71
72 class GreatQuestionBadge(NodeCountableAbstractBadge):
73     type = const.GOLD_BADGE
74     description = _('Question voted up %s times') % str(settings.GREAT_QUESTION_VOTES_UP)
75
76     def __init__(self):
77         super(GreatQuestionBadge, self).__init__("question", 'vote_up_count', settings.GREAT_QUESTION_VOTES_UP)
78
79
80 class FavoriteQuestionBadge(PostCountableAbstractBadge):
81     type = const.SILVER_BADGE
82     description = _('Question favorited by %s users') % str(settings.FAVORITE_QUESTION_FAVS)
83
84     def __init__(self):
85         super(FavoriteQuestionBadge, self).__init__(Question, 'favourite_count', settings.FAVORITE_QUESTION_FAVS)
86
87 class StellarQuestionBadge(PostCountableAbstractBadge):
88     type = const.GOLD_BADGE
89     description = _('Question favorited by %s users') % str(settings.STELLAR_QUESTION_FAVS)
90
91     def __init__(self):
92         super(StellarQuestionBadge, self).__init__(Question, 'favourite_count', settings.STELLAR_QUESTION_FAVS)
93
94
95 class DisciplinedBadge(ActivityAbstractBadge):
96     type = const.BRONZE_BADGE
97     description = _('Deleted own post with score of %s or higher') % str(settings.DISCIPLINED_MIN_SCORE)
98
99     def __init__(self):
100         def handler(instance):
101             if instance.user.id == instance.content_object.author.id and instance.content_object.score >= settings.DISCIPLINED_MIN_SCORE:
102                 self.award_badge(instance.user, instance)
103
104         super(DisciplinedBadge, self).__init__(const.TYPE_ACTIVITY_DELETE_QUESTION, handler)
105
106 class PeerPressureBadge(ActivityAbstractBadge):
107     type = const.BRONZE_BADGE
108     description = _('Deleted own post with score of %s or lower') % str(settings.PEER_PRESSURE_MAX_SCORE)
109
110     def __init__(self):
111         def handler(instance):
112             if instance.user.id == instance.content_object.author.id and instance.content_object.score <= settings.PEER_PRESSURE_MAX_SCORE:
113                 self.award_badge(instance.user, instance)
114
115         super(PeerPressureBadge, self).__init__(const.TYPE_ACTIVITY_DELETE_QUESTION, handler)
116
117
118 class CitizenPatrolBadge(FirstActivityAbstractBadge):
119     type = const.BRONZE_BADGE
120     description = _('First flagged post')
121
122     def __init__(self):
123         super(CitizenPatrolBadge, self).__init__(const.TYPE_ACTIVITY_MARK_OFFENSIVE)
124
125 class CriticBadge(FirstActivityAbstractBadge):
126     type = const.BRONZE_BADGE
127     description = _('First down vote')
128
129     def __init__(self):
130         super(CriticBadge, self).__init__(const.TYPE_ACTIVITY_VOTE_DOWN)
131
132 class OrganizerBadge(FirstActivityAbstractBadge):
133     type = const.BRONZE_BADGE
134     description = _('First retag')
135
136     def __init__(self):
137         super(OrganizerBadge, self).__init__(const.TYPE_ACTIVITY_UPDATE_TAGS)
138
139 class SupporterBadge(FirstActivityAbstractBadge):
140     type = const.BRONZE_BADGE
141     description = _('First up vote')
142
143     def __init__(self):
144         super(SupporterBadge, self).__init__(const.TYPE_ACTIVITY_VOTE_UP)
145
146 class EditorBadge(FirstActivityAbstractBadge):
147     type = const.BRONZE_BADGE
148     description = _('First edit')
149
150     def __init__(self):
151         super(EditorBadge, self).__init__((const.TYPE_ACTIVITY_UPDATE_ANSWER, const.TYPE_ACTIVITY_UPDATE_QUESTION))
152
153 class ScholarBadge(FirstActivityAbstractBadge):
154     type = const.BRONZE_BADGE
155     description = _('First accepted answer on your own question')
156
157     def __init__(self):
158         super(ScholarBadge, self).__init__(const.TYPE_ACTIVITY_MARK_ANSWER)
159
160 class AutobiographerBadge(FirstActivityAbstractBadge):
161     type = const.BRONZE_BADGE
162     description = _('Completed all user profile fields')
163
164     def __init__(self):
165         super(AutobiographerBadge, self).__init__(const.TYPE_ACTIVITY_USER_FULL_UPDATED)
166
167 class CleanupBadge(FirstActivityAbstractBadge):
168     type = const.BRONZE_BADGE
169     description = _('First rollback')
170
171     def __init__(self):
172         super(CleanupBadge, self).__init__((const.TYPE_ACTIVITY_CANCEL_VOTE_UP, const.TYPE_ACTIVITY_CANCEL_VOTE_DOWN))
173
174
175 class CivicDutyBadge(ActivityCountAbstractBadge):
176     type = const.SILVER_BADGE
177     description = _('Voted %s times') % str(settings.CIVIC_DUTY_VOTES)
178
179     def __init__(self):
180         super(CivicDutyBadge, self).__init__((const.TYPE_ACTIVITY_VOTE_DOWN, const.TYPE_ACTIVITY_VOTE_UP), settings.CIVIC_DUTY_VOTES)
181
182 class PunditBadge(ActivityCountAbstractBadge):
183     type = const.BRONZE_BADGE
184     description = _('Left %s comments') % str(settings.PUNDIT_COMMENT_COUNT)
185
186     def __init__(self):
187         super(PunditBadge, self).__init__((const.TYPE_ACTIVITY_COMMENT_ANSWER, const.TYPE_ACTIVITY_COMMENT_QUESTION), settings.PUNDIT_COMMENT_COUNT)
188
189
190 class SelfLearnerBadge(CountableAbstractBadge):
191     type = const.BRONZE_BADGE
192     description = _('Answered your own question with at least %s up votes') % str(settings.SELF_LEARNER_UP_VOTES)
193
194     def __init__(self):
195
196         def handler(instance):
197             if instance.node_type == "answer" and instance.author_id == instance.question.author_id:
198                 self.award_badge(instance.author, instance)
199
200         super(SelfLearnerBadge, self).__init__(Node, 'vote_up_count', settings.SELF_LEARNER_UP_VOTES, handler)
201
202
203 class StrunkAndWhiteBadge(ActivityCountAbstractBadge):
204     type = const.SILVER_BADGE
205     name = _('Strunk & White')
206     description = _('Edited %s entries') % str(settings.STRUNK_AND_WHITE_EDITS)
207
208     def __init__(self):
209         super(StrunkAndWhiteBadge, self).__init__((const.TYPE_ACTIVITY_UPDATE_ANSWER, const.TYPE_ACTIVITY_UPDATE_QUESTION), settings.STRUNK_AND_WHITE_EDITS)
210
211
212 def is_user_first(post):
213     return post.__class__.objects.filter(author=post.author).order_by('added_at')[0].id == post.id
214
215 class StudentBadge(CountableAbstractBadge):
216     type = const.BRONZE_BADGE
217     description = _('Asked first question with at least one up vote')
218
219     def __init__(self):
220         def handler(instance):
221             if instance.node_type == "question" and is_user_first(instance):
222                 self.award_badge(instance.author, instance)
223
224         super(StudentBadge, self).__init__(Node, 'vote_up_count', 1, handler)
225
226 class TeacherBadge(CountableAbstractBadge):
227     type = const.BRONZE_BADGE
228     description = _('Answered first question with at least one up vote')
229
230     def __init__(self):
231         def handler(instance):
232             if instance.node_type == "answer" and is_user_first(instance):
233                 self.award_badge(instance.author, instance)
234
235         super(TeacherBadge, self).__init__(Node, 'vote_up_count', 1, handler)
236
237
238 class AcceptedAndVotedAnswerAbstractBadge(AbstractBadge):
239     def __init__(self, up_votes, handler):
240         def wrapper(sender, instance, **kwargs):
241             if sender is Answer:
242                 if (not kwargs['field'] == "score") or (kwargs['new'] < kwargs['old']):
243                     return
244
245                 answer = instance.leaf
246                 vote_count = kwargs['new']
247             else:
248                 answer = instance.content_object
249                 vote_count = answer.vote_up_count
250
251             if answer.accepted and vote_count == up_votes:
252                 handler(answer)
253
254         activity_record.connect(wrapper, sender=const.TYPE_ACTIVITY_MARK_ANSWER, weak=False)
255         denorm_update.connect(wrapper, sender=Node, weak=False)
256
257
258 class EnlightenedBadge(AcceptedAndVotedAnswerAbstractBadge):
259     type = const.SILVER_BADGE
260     description = _('First answer was accepted with at least %s up votes') % str(settings.ENLIGHTENED_UP_VOTES)
261
262     def __init__(self):
263         def handler(answer):
264             self.award_badge(answer.author, answer, True)
265
266         super(EnlightenedBadge, self).__init__(settings.ENLIGHTENED_UP_VOTES, handler)
267
268
269 class GuruBadge(AcceptedAndVotedAnswerAbstractBadge):
270     type = const.SILVER_BADGE
271     description = _('Accepted answer and voted up %s times') % str(settings.GURU_UP_VOTES)
272
273     def __init__(self):
274         def handler(answer):
275             self.award_badge(answer.author, answer)
276
277         super(GuruBadge, self).__init__(settings.GURU_UP_VOTES, handler)
278
279
280 class NecromancerBadge(CountableAbstractBadge):
281     type = const.SILVER_BADGE
282     description = _('Answered a question more than %(dif_days)s days later with at least %(up_votes)s votes') % \
283             {'dif_days': str(settings.NECROMANCER_DIF_DAYS), 'up_votes': str(settings.NECROMANCER_UP_VOTES)}
284
285     def __init__(self):
286         def handler(instance):
287             if instance.node_type == "answer" and instance.added_at >= (instance.question.added_at + timedelta(days=int(settings.NECROMANCER_DIF_DAYS))):
288                 self.award_badge(instance.author, instance)
289
290         super(NecromancerBadge, self).__init__(Node, "vote_up_count", settings.NECROMANCER_UP_VOTES, handler)
291
292
293 class TaxonomistBadge(AbstractBadge):
294     type = const.SILVER_BADGE
295     description = _('Created a tag used by %s questions') % str(settings.TAXONOMIST_USE_COUNT)
296
297     def __init__(self):
298         def handler(instance, **kwargs):
299             if instance.used_count == settings.TAXONOMIST_USE_COUNT:
300                 self.award_badge(instance.created_by, instance)           
301
302         post_save.connect(handler, sender=Tag, weak=False)
303
304
305 #class GeneralistTag(AbstractBadge):
306 #    pass
307
308 #class ExpertTag(AbstractBadge):
309 #    pass
310
311 #class YearlingTag(AbstractBadge):
312 #    pass
313
314
315