1 from datetime import timedelta
3 from django.db.models.signals import post_save
4 from django.utils.translation import ugettext as _
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
15 class PopularQuestionBadge(PostCountableAbstractBadge):
16 type = const.BRONZE_BADGE
17 description = _('Asked a question with %s views') % str(settings.POPULAR_QUESTION_VIEWS)
20 super(PopularQuestionBadge, self).__init__(Question, 'view_count', settings.POPULAR_QUESTION_VIEWS)
22 class NotableQuestionBadge(PostCountableAbstractBadge):
23 type = const.SILVER_BADGE
24 description = _('Asked a question with %s views') % str(settings.NOTABLE_QUESTION_VIEWS)
27 super(NotableQuestionBadge, self).__init__(Question, 'view_count', settings.NOTABLE_QUESTION_VIEWS)
29 class FamousQuestionBadge(PostCountableAbstractBadge):
30 type = const.GOLD_BADGE
31 description = _('Asked a question with %s views') % str(settings.FAMOUS_QUESTION_VIEWS)
34 super(FamousQuestionBadge, self).__init__(Question, 'view_count', settings.FAMOUS_QUESTION_VIEWS)
37 class NiceAnswerBadge(NodeCountableAbstractBadge):
38 type = const.BRONZE_BADGE
39 description = _('Answer voted up %s times') % str(settings.NICE_ANSWER_VOTES_UP)
42 super(NiceAnswerBadge, self).__init__("answer", 'vote_up_count', settings.NICE_ANSWER_VOTES_UP)
44 class NiceQuestionBadge(NodeCountableAbstractBadge):
45 type = const.BRONZE_BADGE
46 description = _('Question voted up %s times') % str(settings.NICE_QUESTION_VOTES_UP)
49 super(NiceQuestionBadge, self).__init__("question", 'vote_up_count', settings.NICE_QUESTION_VOTES_UP)
51 class GoodAnswerBadge(NodeCountableAbstractBadge):
52 type = const.SILVER_BADGE
53 description = _('Answer voted up %s times') % str(settings.GOOD_ANSWER_VOTES_UP)
56 super(GoodAnswerBadge, self).__init__("answer", 'vote_up_count', settings.GOOD_ANSWER_VOTES_UP)
58 class GoodQuestionBadge(NodeCountableAbstractBadge):
59 type = const.SILVER_BADGE
60 description = _('Question voted up %s times') % str(settings.GOOD_QUESTION_VOTES_UP)
63 super(GoodQuestionBadge, self).__init__("question", 'vote_up_count', settings.GOOD_QUESTION_VOTES_UP)
65 class GreatAnswerBadge(NodeCountableAbstractBadge):
66 type = const.GOLD_BADGE
67 description = _('Answer voted up %s times') % str(settings.GREAT_ANSWER_VOTES_UP)
70 super(GreatAnswerBadge, self).__init__("answer", 'vote_up_count', settings.GREAT_ANSWER_VOTES_UP)
72 class GreatQuestionBadge(NodeCountableAbstractBadge):
73 type = const.GOLD_BADGE
74 description = _('Question voted up %s times') % str(settings.GREAT_QUESTION_VOTES_UP)
77 super(GreatQuestionBadge, self).__init__("question", 'vote_up_count', settings.GREAT_QUESTION_VOTES_UP)
80 class FavoriteQuestionBadge(PostCountableAbstractBadge):
81 type = const.SILVER_BADGE
82 description = _('Question favorited by %s users') % str(settings.FAVORITE_QUESTION_FAVS)
85 super(FavoriteQuestionBadge, self).__init__(Question, 'favourite_count', settings.FAVORITE_QUESTION_FAVS)
87 class StellarQuestionBadge(PostCountableAbstractBadge):
88 type = const.GOLD_BADGE
89 description = _('Question favorited by %s users') % str(settings.STELLAR_QUESTION_FAVS)
92 super(StellarQuestionBadge, self).__init__(Question, 'favourite_count', settings.STELLAR_QUESTION_FAVS)
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)
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)
104 super(DisciplinedBadge, self).__init__(const.TYPE_ACTIVITY_DELETE_QUESTION, handler)
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)
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)
115 super(PeerPressureBadge, self).__init__(const.TYPE_ACTIVITY_DELETE_QUESTION, handler)
118 class CitizenPatrolBadge(FirstActivityAbstractBadge):
119 type = const.BRONZE_BADGE
120 description = _('First flagged post')
123 super(CitizenPatrolBadge, self).__init__(const.TYPE_ACTIVITY_MARK_OFFENSIVE)
125 class CriticBadge(FirstActivityAbstractBadge):
126 type = const.BRONZE_BADGE
127 description = _('First down vote')
130 super(CriticBadge, self).__init__(const.TYPE_ACTIVITY_VOTE_DOWN)
132 class OrganizerBadge(FirstActivityAbstractBadge):
133 type = const.BRONZE_BADGE
134 description = _('First retag')
137 super(OrganizerBadge, self).__init__(const.TYPE_ACTIVITY_UPDATE_TAGS)
139 class SupporterBadge(FirstActivityAbstractBadge):
140 type = const.BRONZE_BADGE
141 description = _('First up vote')
144 super(SupporterBadge, self).__init__(const.TYPE_ACTIVITY_VOTE_UP)
146 class EditorBadge(FirstActivityAbstractBadge):
147 type = const.BRONZE_BADGE
148 description = _('First edit')
151 super(EditorBadge, self).__init__((const.TYPE_ACTIVITY_UPDATE_ANSWER, const.TYPE_ACTIVITY_UPDATE_QUESTION))
153 class ScholarBadge(FirstActivityAbstractBadge):
154 type = const.BRONZE_BADGE
155 description = _('First accepted answer on your own question')
158 super(ScholarBadge, self).__init__(const.TYPE_ACTIVITY_MARK_ANSWER)
160 class AutobiographerBadge(FirstActivityAbstractBadge):
161 type = const.BRONZE_BADGE
162 description = _('Completed all user profile fields')
165 super(AutobiographerBadge, self).__init__(const.TYPE_ACTIVITY_USER_FULL_UPDATED)
167 class CleanupBadge(FirstActivityAbstractBadge):
168 type = const.BRONZE_BADGE
169 description = _('First rollback')
172 super(CleanupBadge, self).__init__((const.TYPE_ACTIVITY_CANCEL_VOTE_UP, const.TYPE_ACTIVITY_CANCEL_VOTE_DOWN))
175 class CivicDutyBadge(ActivityCountAbstractBadge):
176 type = const.SILVER_BADGE
177 description = _('Voted %s times') % str(settings.CIVIC_DUTY_VOTES)
180 super(CivicDutyBadge, self).__init__((const.TYPE_ACTIVITY_VOTE_DOWN, const.TYPE_ACTIVITY_VOTE_UP), settings.CIVIC_DUTY_VOTES)
182 class PunditBadge(ActivityCountAbstractBadge):
183 type = const.BRONZE_BADGE
184 description = _('Left %s comments') % str(settings.PUNDIT_COMMENT_COUNT)
187 super(PunditBadge, self).__init__((const.TYPE_ACTIVITY_COMMENT_ANSWER, const.TYPE_ACTIVITY_COMMENT_QUESTION), settings.PUNDIT_COMMENT_COUNT)
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)
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)
200 super(SelfLearnerBadge, self).__init__(Node, 'vote_up_count', settings.SELF_LEARNER_UP_VOTES, handler)
203 class StrunkAndWhiteBadge(ActivityCountAbstractBadge):
204 type = const.SILVER_BADGE
205 name = _('Strunk & White')
206 description = _('Edited %s entries') % str(settings.STRUNK_AND_WHITE_EDITS)
209 super(StrunkAndWhiteBadge, self).__init__((const.TYPE_ACTIVITY_UPDATE_ANSWER, const.TYPE_ACTIVITY_UPDATE_QUESTION), settings.STRUNK_AND_WHITE_EDITS)
212 def is_user_first(post):
213 return post.__class__.objects.filter(author=post.author).order_by('added_at')[0].id == post.id
215 class StudentBadge(CountableAbstractBadge):
216 type = const.BRONZE_BADGE
217 description = _('Asked first question with at least one up vote')
220 def handler(instance):
221 if instance.node_type == "question" and is_user_first(instance):
222 self.award_badge(instance.author, instance)
224 super(StudentBadge, self).__init__(Node, 'vote_up_count', 1, handler)
226 class TeacherBadge(CountableAbstractBadge):
227 type = const.BRONZE_BADGE
228 description = _('Answered first question with at least one up vote')
231 def handler(instance):
232 if instance.node_type == "answer" and is_user_first(instance):
233 self.award_badge(instance.author, instance)
235 super(TeacherBadge, self).__init__(Node, 'vote_up_count', 1, handler)
238 class AcceptedAndVotedAnswerAbstractBadge(AbstractBadge):
239 def __init__(self, up_votes, handler):
240 def wrapper(sender, instance, **kwargs):
242 if (not kwargs['field'] == "score") or (kwargs['new'] < kwargs['old']):
245 answer = instance.leaf
246 vote_count = kwargs['new']
248 answer = instance.content_object
249 vote_count = answer.vote_up_count
251 if answer.accepted and vote_count == up_votes:
254 activity_record.connect(wrapper, sender=const.TYPE_ACTIVITY_MARK_ANSWER, weak=False)
255 denorm_update.connect(wrapper, sender=Node, weak=False)
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)
264 self.award_badge(answer.author, answer, True)
266 super(EnlightenedBadge, self).__init__(settings.ENLIGHTENED_UP_VOTES, handler)
269 class GuruBadge(AcceptedAndVotedAnswerAbstractBadge):
270 type = const.SILVER_BADGE
271 description = _('Accepted answer and voted up %s times') % str(settings.GURU_UP_VOTES)
275 self.award_badge(answer.author, answer)
277 super(GuruBadge, self).__init__(settings.GURU_UP_VOTES, handler)
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)}
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)
290 super(NecromancerBadge, self).__init__(Node, "vote_up_count", settings.NECROMANCER_UP_VOTES, handler)
293 class TaxonomistBadge(AbstractBadge):
294 type = const.SILVER_BADGE
295 description = _('Created a tag used by %s questions') % str(settings.TAXONOMIST_USE_COUNT)
298 def handler(instance, **kwargs):
299 if instance.used_count == settings.TAXONOMIST_USE_COUNT:
300 self.award_badge(instance.created_by, instance)
302 post_save.connect(handler, sender=Tag, weak=False)
305 #class GeneralistTag(AbstractBadge):
308 #class ExpertTag(AbstractBadge):
311 #class YearlingTag(AbstractBadge):