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
8 from forum.models import Question, Answer, Activity, Tag
9 from forum.models.user import activity_record
10 from forum import const
14 class PopularQuestionBadge(PostCountableAbstractBadge):
15 type = const.BRONZE_BADGE
16 description = _('Asked a question with %s views') % str(settings.POPULAR_QUESTION_VIEWS)
19 super(PopularQuestionBadge, self).__init__(Question, 'view_count', settings.POPULAR_QUESTION_VIEWS)
21 class NotableQuestionBadge(PostCountableAbstractBadge):
22 type = const.SILVER_BADGE
23 description = _('Asked a question with %s views') % str(settings.NOTABLE_QUESTION_VIEWS)
26 super(NotableQuestionBadge, self).__init__(Question, 'view_count', settings.NOTABLE_QUESTION_VIEWS)
28 class FamousQuestionBadge(PostCountableAbstractBadge):
29 type = const.GOLD_BADGE
30 description = _('Asked a question with %s views') % str(settings.FAMOUS_QUESTION_VIEWS)
33 super(FamousQuestionBadge, self).__init__(Question, 'view_count', settings.FAMOUS_QUESTION_VIEWS)
36 class NiceAnswerBadge(PostCountableAbstractBadge):
37 type = const.BRONZE_BADGE
38 description = _('Answer voted up %s times') % str(settings.NICE_ANSWER_VOTES_UP)
41 super(NiceAnswerBadge, self).__init__(Answer, 'vote_up_count', settings.NICE_ANSWER_VOTES_UP)
43 class NiceQuestionBadge(PostCountableAbstractBadge):
44 type = const.BRONZE_BADGE
45 description = _('Question voted up %s times') % str(settings.NICE_QUESTION_VOTES_UP)
48 super(NiceQuestionBadge, self).__init__(Question, 'vote_up_count', settings.NICE_QUESTION_VOTES_UP)
50 class GoodAnswerBadge(PostCountableAbstractBadge):
51 type = const.SILVER_BADGE
52 description = _('Answer voted up %s times') % str(settings.GOOD_ANSWER_VOTES_UP)
55 super(GoodAnswerBadge, self).__init__(Answer, 'vote_up_count', settings.GOOD_ANSWER_VOTES_UP)
57 class GoodQuestionBadge(PostCountableAbstractBadge):
58 type = const.SILVER_BADGE
59 description = _('Question voted up %s times') % str(settings.GOOD_QUESTION_VOTES_UP)
62 super(GoodQuestionBadge, self).__init__(Question, 'vote_up_count', settings.GOOD_QUESTION_VOTES_UP)
64 class GreatAnswerBadge(PostCountableAbstractBadge):
65 type = const.GOLD_BADGE
66 description = _('Answer voted up %s times') % str(settings.GREAT_ANSWER_VOTES_UP)
69 super(GreatAnswerBadge, self).__init__(Answer, 'vote_up_count', settings.GREAT_ANSWER_VOTES_UP)
71 class GreatQuestionBadge(PostCountableAbstractBadge):
72 type = const.GOLD_BADGE
73 description = _('Question voted up %s times') % str(settings.GREAT_QUESTION_VOTES_UP)
76 super(GreatQuestionBadge, self).__init__(Question, 'vote_up_count', settings.GREAT_QUESTION_VOTES_UP)
79 class FavoriteQuestionBadge(PostCountableAbstractBadge):
80 type = const.SILVER_BADGE
81 description = _('Question favorited by %s users') % str(settings.FAVORITE_QUESTION_FAVS)
84 super(FavoriteQuestionBadge, self).__init__(Question, 'favourite_count', settings.FAVORITE_QUESTION_FAVS)
86 class StellarQuestionBadge(PostCountableAbstractBadge):
87 type = const.GOLD_BADGE
88 description = _('Question favorited by %s users') % str(settings.STELLAR_QUESTION_FAVS)
91 super(StellarQuestionBadge, self).__init__(Question, 'favourite_count', settings.STELLAR_QUESTION_FAVS)
94 class DisciplinedBadge(ActivityAbstractBadge):
95 type = const.BRONZE_BADGE
96 description = _('Deleted own post with score of %s or higher') % str(settings.DISCIPLINED_MIN_SCORE)
99 def handler(instance):
100 if instance.user.id == instance.content_object.author.id and instance.content_object.score >= settings.DISCIPLINED_MIN_SCORE:
101 self.award_badge(instance.user, instance)
103 super(DisciplinedBadge, self).__init__(const.TYPE_ACTIVITY_DELETE_QUESTION, handler)
105 class PeerPressureBadge(ActivityAbstractBadge):
106 type = const.BRONZE_BADGE
107 description = _('Deleted own post with score of %s or lower') % str(settings.PEER_PRESSURE_MAX_SCORE)
110 def handler(instance):
111 if instance.user.id == instance.content_object.author.id and instance.content_object.score <= settings.PEER_PRESSURE_MAX_SCORE:
112 self.award_badge(instance.user, instance)
114 super(PeerPressureBadge, self).__init__(const.TYPE_ACTIVITY_DELETE_QUESTION, handler)
117 class CitizenPatrolBadge(FirstActivityAbstractBadge):
118 type = const.BRONZE_BADGE
119 description = _('First flagged post')
122 super(CitizenPatrolBadge, self).__init__(const.TYPE_ACTIVITY_MARK_OFFENSIVE)
124 class CriticBadge(FirstActivityAbstractBadge):
125 type = const.BRONZE_BADGE
126 description = _('First down vote')
129 super(CriticBadge, self).__init__(const.TYPE_ACTIVITY_VOTE_DOWN)
131 class OrganizerBadge(FirstActivityAbstractBadge):
132 type = const.BRONZE_BADGE
133 description = _('First retag')
136 super(OrganizerBadge, self).__init__(const.TYPE_ACTIVITY_UPDATE_TAGS)
138 class SupporterBadge(FirstActivityAbstractBadge):
139 type = const.BRONZE_BADGE
140 description = _('First up vote')
143 super(SupporterBadge, self).__init__(const.TYPE_ACTIVITY_VOTE_UP)
145 class EditorBadge(FirstActivityAbstractBadge):
146 type = const.BRONZE_BADGE
147 description = _('First edit')
150 super(EditorBadge, self).__init__((const.TYPE_ACTIVITY_UPDATE_ANSWER, const.TYPE_ACTIVITY_UPDATE_QUESTION))
152 class ScholarBadge(FirstActivityAbstractBadge):
153 type = const.BRONZE_BADGE
154 description = _('First accepted answer on your own question')
157 super(ScholarBadge, self).__init__(const.TYPE_ACTIVITY_MARK_ANSWER)
159 class AutobiographerBadge(FirstActivityAbstractBadge):
160 type = const.BRONZE_BADGE
161 description = _('Completed all user profile fields')
164 super(AutobiographerBadge, self).__init__(const.TYPE_ACTIVITY_USER_FULL_UPDATED)
166 class CleanupBadge(FirstActivityAbstractBadge):
167 type = const.BRONZE_BADGE
168 description = _('First rollback')
171 super(CleanupBadge, self).__init__((const.TYPE_ACTIVITY_CANCEL_VOTE_UP, const.TYPE_ACTIVITY_CANCEL_VOTE_DOWN))
174 class CivicDutyBadge(ActivityCountAbstractBadge):
175 type = const.SILVER_BADGE
176 description = _('Voted %s times') % str(settings.CIVIC_DUTY_VOTES)
179 super(CivicDutyBadge, self).__init__((const.TYPE_ACTIVITY_VOTE_DOWN, const.TYPE_ACTIVITY_VOTE_UP), settings.CIVIC_DUTY_VOTES)
181 class PunditBadge(ActivityCountAbstractBadge):
182 type = const.BRONZE_BADGE
183 description = _('Left %s comments') % str(settings.PUNDIT_COMMENT_COUNT)
186 super(PunditBadge, self).__init__((const.TYPE_ACTIVITY_COMMENT_ANSWER, const.TYPE_ACTIVITY_COMMENT_QUESTION), settings.PUNDIT_COMMENT_COUNT)
189 class SelfLearnerBadge(CountableAbstractBadge):
190 type = const.BRONZE_BADGE
191 description = _('Answered your own question with at least %s up votes') % str(settings.SELF_LEARNER_UP_VOTES)
195 def handler(instance):
196 if instance.author_id == instance.question.author_id:
197 self.award_badge(instance.author, instance)
199 super(SelfLearnerBadge, self).__init__(Answer, 'vote_up_count', settings.SELF_LEARNER_UP_VOTES, handler)
202 class StrunkAndWhiteBadge(ActivityCountAbstractBadge):
203 type = const.SILVER_BADGE
204 name = _('Strunk & White')
205 description = _('Edited %s entries') % str(settings.STRUNK_AND_WHITE_EDITS)
208 super(StrunkAndWhiteBadge, self).__init__((const.TYPE_ACTIVITY_UPDATE_ANSWER, const.TYPE_ACTIVITY_UPDATE_QUESTION), settings.STRUNK_AND_WHITE_EDITS)
211 def is_user_first(post):
212 return post.__class__.objects.filter(author=post.author).order_by('added_at')[0].id == post.id
214 class StudentBadge(CountableAbstractBadge):
215 type = const.BRONZE_BADGE
216 description = _('Asked first question with at least one up vote')
219 def handler(instance):
220 if is_user_first(instance):
221 self.award_badge(instance.author, instance)
223 super(StudentBadge, self).__init__(Question, 'vote_up_count', 1, handler)
225 class TeacherBadge(CountableAbstractBadge):
226 type = const.BRONZE_BADGE
227 description = _('Answered first question with at least one up vote')
230 def handler(instance):
231 if is_user_first(instance):
232 self.award_badge(instance.author, instance)
234 super(TeacherBadge, self).__init__(Answer, 'vote_up_count', 1, handler)
237 class AcceptedAndVotedAnswerAbstractBadge(AbstractBadge):
238 def __init__(self, up_votes, handler):
239 def wrapper(sender, instance, **kwargs):
242 if not "vote_up_count" in answer.get_dirty_fields():
245 answer = instance.content_object
247 accepted = answer.accepted
248 vote_count = answer.vote_up_count
250 if accepted and vote_count == up_votes:
253 activity_record.connect(wrapper, sender=const.TYPE_ACTIVITY_MARK_ANSWER, weak=False)
254 post_save.connect(wrapper, sender=Answer, weak=False)
257 class EnlightenedBadge(AcceptedAndVotedAnswerAbstractBadge):
258 type = const.SILVER_BADGE
259 description = _('First answer was accepted with at least %s up votes') % str(settings.ENLIGHTENED_UP_VOTES)
263 self.award_badge(answer.author, answer, True)
265 super(EnlightenedBadge, self).__init__(settings.ENLIGHTENED_UP_VOTES, handler)
268 class GuruBadge(AcceptedAndVotedAnswerAbstractBadge):
269 type = const.SILVER_BADGE
270 description = _('Accepted answer and voted up %s times') % str(settings.GURU_UP_VOTES)
274 self.award_badge(answer.author, answer)
276 super(GuruBadge, self).__init__(settings.GURU_UP_VOTES, handler)
279 class NecromancerBadge(CountableAbstractBadge):
280 type = const.SILVER_BADGE
281 description = _('Answered a question more than %(dif_days)s days later with at least %(up_votes)s votes') % \
282 {'dif_days': str(settings.NECROMANCER_DIF_DAYS), 'up_votes': str(settings.NECROMANCER_UP_VOTES)}
285 def handler(instance):
286 if instance.added_at >= (instance.question.added_at + timedelta(days=int(settings.NECROMANCER_DIF_DAYS))):
287 self.award_badge(instance.author, instance)
289 super(NecromancerBadge, self).__init__(Answer, "vote_up_count", settings.NECROMANCER_UP_VOTES, handler)
292 class TaxonomistBadge(AbstractBadge):
293 type = const.SILVER_BADGE
294 description = _('Created a tag used by %s questions') % str(settings.TAXONOMIST_USE_COUNT)
297 def handler(instance, **kwargs):
298 if instance.used_count == settings.TAXONOMIST_USE_COUNT:
299 self.award_badge(instance.created_by, instance)
301 post_save.connect(handler, sender=Tag, weak=False)
304 #class GeneralistTag(AbstractBadge):
307 #class ExpertTag(AbstractBadge):
310 #class YearlingTag(AbstractBadge):