summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEvgeny Fadeev <evgeny.fadeev@gmail.com>2010-12-03 22:10:01 -0500
committerEvgeny Fadeev <evgeny.fadeev@gmail.com>2010-12-03 22:10:01 -0500
commit11f2cc0579716d3f830e2a82d33a3f1162108dbf (patch)
treece98496a84f7d93c68ef1d198d904241af4b57cf
parent60bc97227cf407e7bf12c5f249c6d7f73cef5276 (diff)
downloadaskbot-11f2cc0579716d3f830e2a82d33a3f1162108dbf.tar.gz
askbot-11f2cc0579716d3f830e2a82d33a3f1162108dbf.tar.bz2
askbot-11f2cc0579716d3f830e2a82d33a3f1162108dbf.zip
half of the badges are redone
-rw-r--r--askbot/auth.py3
-rw-r--r--askbot/conf/badges.py29
-rw-r--r--askbot/importers/stackexchange/management/commands/load_stackexchange.py2
-rw-r--r--askbot/models/__init__.py15
-rw-r--r--askbot/models/badges.py137
-rw-r--r--askbot/tests/badge_tests.py105
6 files changed, 258 insertions, 33 deletions
diff --git a/askbot/auth.py b/askbot/auth.py
index 05403634..7b4fac68 100644
--- a/askbot/auth.py
+++ b/askbot/auth.py
@@ -121,9 +121,6 @@ def onFlaggedItem(post, user, timestamp=None):
@transaction.commit_on_success
def onAnswerAccept(answer, user, timestamp=None):
- if timestamp is None:
- timestamp = datetime.datetime.now()
-
answer.accepted = True
answer.accepted_at = timestamp
answer.question.answer_accepted = True
diff --git a/askbot/conf/badges.py b/askbot/conf/badges.py
index d5f8313a..7019a69f 100644
--- a/askbot/conf/badges.py
+++ b/askbot/conf/badges.py
@@ -125,7 +125,34 @@ settings.register(
IntegerValue(
BADGES,
'SELF_LEARNER_BADGE_MIN_UPVOTES',
- default=500,
+ default=1,
description=_('Self-Learner: minimum answer upvotes')
)
)
+
+settings.register(
+ IntegerValue(
+ BADGES,
+ 'CIVIC_DUTY_BADGE_MIN_VOTES',
+ default=100,
+ description=_('Civic Duty: minimum votes')
+ )
+)
+
+settings.register(
+ IntegerValue(
+ BADGES,
+ 'ENLIGHTENED_BADGE_MIN_UPVOTES',
+ default=3,
+ description=_('Enlightened Duty: minimum upvotes')
+ )
+)
+
+settings.register(
+ IntegerValue(
+ BADGES,
+ 'GURU_BADGE_MIN_UPVOTES',
+ default=5,
+ description=_('Guru: minimum upvotes')
+ )
+)
diff --git a/askbot/importers/stackexchange/management/commands/load_stackexchange.py b/askbot/importers/stackexchange/management/commands/load_stackexchange.py
index a39a9cd6..96327323 100644
--- a/askbot/importers/stackexchange/management/commands/load_stackexchange.py
+++ b/askbot/importers/stackexchange/management/commands/load_stackexchange.py
@@ -59,7 +59,7 @@ class X(object):#
vote_actions = {
'UpMod':'upvote',
'DownMod':'downvote',
- 'AcceptedByOriginator':'accept_answer',
+ 'AcceptedByOriginator':'accept_best_answer',
'Offensive':'flag_post',
'Favorite':'toggle_favorite_question',
}
diff --git a/askbot/models/__init__.py b/askbot/models/__init__.py
index 43982e37..4280e650 100644
--- a/askbot/models/__init__.py
+++ b/askbot/models/__init__.py
@@ -745,7 +745,13 @@ def user_accept_best_answer(self, answer = None, timestamp = None):
for prev_answer in prev_accepted_answers:
auth.onAnswerAcceptCanceled(prev_answer, self)
- auth.onAnswerAccept(answer, self)
+ auth.onAnswerAccept(answer, self, timestamp = timestamp)
+ award_badges_signal.send(None,
+ event = 'accept_best_answer',
+ actor = self,
+ context_object = answer,
+ timestamp = timestamp
+ )
@auto_now_timestamp
def user_unaccept_best_answer(self, answer = None, timestamp = None):
@@ -1414,12 +1420,6 @@ def downvote(self, post, timestamp=None, cancel=False):
vote_type=Vote.VOTE_DOWN
)
-def accept_answer(self, answer, timestamp=None, cancel=False):
- if cancel:
- auth.onAnswerAcceptCanceled(answer, self, timestamp=timestamp)
- else:
- auth.onAnswerAccept(answer, self, timestamp=timestamp)
-
@auto_now_timestamp
def flag_post(user, post, timestamp=None, cancel=False):
if cancel:#todo: can't unflag?
@@ -1499,7 +1499,6 @@ User.add_to_class('delete_post', user_delete_post)
User.add_to_class('visit_question', user_visit_question)
User.add_to_class('upvote', upvote)
User.add_to_class('downvote', downvote)
-User.add_to_class('accept_answer', accept_answer)
User.add_to_class('flag_post', flag_post)
User.add_to_class('get_flags', user_get_flags)
User.add_to_class('get_flag_count_posted_today', user_get_flag_count_posted_today)
diff --git a/askbot/models/badges.py b/askbot/models/badges.py
index 99a10ef2..46f10e04 100644
--- a/askbot/models/badges.py
+++ b/askbot/models/badges.py
@@ -28,6 +28,14 @@ from askbot.utils.decorators import auto_now_timestamp
class Badge(object):
"""base class for the badges
+
+ badges must implement method consider_award
+ which returns a boolean True if award succeds
+ and False otherwise
+
+ consider_award assumes that the function is called
+ upon correct event, i.e. it is the responsibility of
+ the caller to try awarding badges at appropriate times
"""
def __init__(self,
key = '',
@@ -217,6 +225,25 @@ class Critic(FirstVote):
self.description = _('First downvote')
return self
+class CivicDuty(Badge):
+ """awarded once after a certain number of votes"""
+ def __init__(self):
+ min_votes = askbot_settings.CIVIC_DUTY_BADGE_MIN_VOTES
+ super(CivicDuty, self).__init__(
+ key = 'civic-duty',
+ name = _('Civic Duty'),
+ description = _('Voted %(num)s times') % {'num': min_votes},
+ level = const.SILVER_BADGE,
+ multiple = False
+ )
+
+ def consider_award(self, actor = None,
+ context_object = None, timestamp = None):
+ if context_object.post_type not in ('question', 'answer'):
+ return False
+ if actor.votes.count() == askbot_settings.CIVIC_DUTY_BADGE_MIN_VOTES:
+ return self.award(actor, context_object, timestamp)
+
class SelfLearner(Badge):
def __init__(self):
description = _('Answered own question with at least %(num)s up votes')
@@ -412,15 +439,79 @@ class FamousQuestion(FrequentedQuestion):
% {'views' : self.min_views}
return self
-ORIGINAL_DATA = """
- (_('Civic duty'), 2, _('civic-duty'), _('Voted 300 times'), False, 0),
+class Scholar(Badge):
+ """scholar badge is awarded to the asker when
+ he/she accepts an answer for the first time
+ """
+ def __init__(self):
+ description = _('Asked a question and accepted an answer')
+ super(Scholar, self).__init__(
+ key = 'scholar',
+ name = _('Scholar'),
+ level = const.BRONZE_BADGE,
+ multiple = False,
+ description = description
+ )
+
+ def consider_award(self, actor = None,
+ context_object = None, timestamp = None):
+ if context_object.post_type != 'answer':
+ return False
+ answer = context_object
+ if answer.question.author != actor:
+ return False
+ return self.award(actor, context_object, timestamp)
+
+class VotedAcceptedAnswer(Badge):
+ """superclass for Enlightened and Guru badges
+ not awarded directly
+
+ Subclasses must define __new__ function
+ """
+ def __init__(self):
+ super(VotedAcceptedAnswer, self).__init__(
+ key = self.key,
+ name = self.name,
+ level = self.level,
+ multiple = self.multiple,
+ description = self.description
+ )
+
+ def consider_award(self, actor = None,
+ context_object = None, timestamp = None):
+ if context_object.post_type != 'answer':
+ return None
+ answer = context_object
+ if answer.score >= self.min_votes and answer.accepted:
+ return self.award(answer.author, answer, timestamp)
- (_('Enlightened'), 2, _('enlightened'), _('First answer was accepted with at least 10 up votes'), False, 0),
- (_('Guru'), 2, _('guru'), _('Accepted answer and voted up 40 times'), True, 0),
+class Enlightened(VotedAcceptedAnswer):
+ def __new__(cls):
+ self = super(Enlightened, cls).__new__(cls)
+ self.key = 'enlightened'
+ self.name = _('Enlightened')
+ self.level = const.SILVER_BADGE
+ self.multiple = False
+ self.min_votes = askbot_settings.ENLIGHTENED_BADGE_MIN_UPVOTES
+ descr = _('First answer was accepted with %(num)s or more votes')
+ self.description = descr % {'num': self.min_votes}
+ return self
+
+class Guru(VotedAcceptedAnswer):
+ def __new__(cls):
+ self = super(Guru, cls).__new__(cls)
+ self.key = 'guru'
+ self.name = _('Guru')
+ self.level = const.GOLD_BADGE
+ self.multiple = True
+ descr = _('Answer accepted with %(num)s or more votes')
+ self.min_votes = askbot_settings.GURU_BADGE_MIN_UPVOTES
+ self.description = descr % {'num': self.min_votes}
+ return self
+ORIGINAL_DATA = """
(_('Necromancer'), 2, _('necromancer'), _('Answered a question more than 60 days later with at least 5 votes'), True, 0),
- (_('Scholar'), 3, _('scholar'), _('First accepted answer on your own question'), False, 0),
(_('Pundit'), 3, _('pundit'), _('Left 10 comments with score of 10 or more'), False, 0),
(_('Citizen patrol'), 3, _('citizen-patrol'), _('First flagged post'), False, 0),
@@ -435,7 +526,6 @@ ORIGINAL_DATA = """
(_('Stellar Question'), 1, _('stellar-question'), _('Question favorited by 100 users'), True, 0),
(_('Favorite Question'), 2, _('favorite-question'), _('Question favorited by 25 users'), True, 0),
- (_('Alpha'), 2, _('alpha'), _('Actively participated in the private alpha'), False, 0),
(_('Generalist'), 2, _('generalist'), _('Active in many different tags'), False, 0),
(_('Expert'), 2, _('expert'), _('Very active in one tag'), False, 0),
@@ -443,40 +533,47 @@ ORIGINAL_DATA = """
(_('Yearling'), 2, _('yearling'), _('Active member for a year'), False, 0),
(_('Beta'), 2, _('beta'), _('Actively participated in the private beta'), False, 0),
+ (_('Alpha'), 2, _('alpha'), _('Actively participated in the private alpha'), False, 0),
"""
BADGES = {
+ 'critic': Critic,
+ 'civic-duty': CivicDuty,
'disciplined': Disciplined,
- 'peer-pressure': PeerPressure,
- 'teacher': Teacher,
- 'student': Student,
- 'supporter': Supporter,
- 'self-learner': SelfLearner,
- 'nice-answer': NiceAnswer,
+ 'enlightened': Enlightened,
+ 'famous-question': FamousQuestion,
'good-answer': GoodAnswer,
- 'great-answer': GreatAnswer,
- 'nice-question': NiceQuestion,
'good-question': GoodQuestion,
+ 'great-answer': GreatAnswer,
'great-question': GreatQuestion,
- 'popular-question': PopularQuestion,
+ 'guru': Guru,
+ 'nice-answer': NiceAnswer,
+ 'nice-question': NiceQuestion,
'notable-question': NotableQuestion,
- 'famous-question': FamousQuestion,
- 'critic': Critic,
+ 'peer-pressure': PeerPressure,
+ 'popular-question': PopularQuestion,
+ 'scholar': Scholar,
+ 'student': Student,
+ 'supporter': Supporter,
+ 'self-learner': SelfLearner,
+ 'teacher': Teacher,
}
#events are sent as a parameter via signal award_badges_signal
#from appropriate locations in the code of askbot application
#most likely - from manipulator functions that are added to the User objects
EVENTS_TO_BADGES = {
+ 'accept_best_answer': (Scholar, Guru, Enlightened),
'upvote_answer': (
Teacher, NiceAnswer, GoodAnswer,
- GreatAnswer, Supporter, SelfLearner
+ GreatAnswer, Supporter, SelfLearner, CivicDuty,
+ Guru, Enlightened
),
'upvote_question': (
NiceQuestion, GoodQuestion,
- GreatQuestion, Student, Supporter
+ GreatQuestion, Student, Supporter, CivicDuty
),
- 'downvote': (Critic,),#no regard for question or answer for now
+ 'downvote': (Critic, CivicDuty),#no regard for question or answer for now
'delete_post': (Disciplined, PeerPressure,),
'view_question': (PopularQuestion, NotableQuestion, FamousQuestion,),
}
diff --git a/askbot/tests/badge_tests.py b/askbot/tests/badge_tests.py
index be7e604f..1930430d 100644
--- a/askbot/tests/badge_tests.py
+++ b/askbot/tests/badge_tests.py
@@ -16,6 +16,31 @@ class BadgeTests(AskbotTestCase):
count = models.Award.objects.filter(**filters).count()
self.assertEquals(count, expected_count)
+ def assert_accepted_answer_badge_works(self,
+ badge_key = None,
+ min_score = None,
+ expected_count = 1,
+ previous_count = 0,
+ trigger = None
+ ):
+ assert(trigger in ('accept_best_answer', 'upvote_answer'))
+ question = self.post_question(user = self.u1)
+ answer = self.post_answer(user = self.u2, question = question)
+ answer.score = min_score - 1
+ answer.save()
+
+ recipient = answer.author
+
+ if trigger == 'accept_best_answer':
+ self.u1.upvote(answer)
+ self.assert_have_badge(badge_key, recipient, previous_count)
+ self.u1.accept_best_answer(answer)
+ else:
+ self.u1.accept_best_answer(answer)
+ self.assert_have_badge(badge_key, recipient, previous_count)
+ self.u1.upvote(answer)
+ self.assert_have_badge(badge_key, recipient, expected_count)
+
def assert_upvoted_answer_badge_works(self,
badge_key = None,
min_score = None,
@@ -207,3 +232,83 @@ class BadgeTests(AskbotTestCase):
answer.save()
self.u2.upvote(answer)
self.assert_have_badge('self-learner', recipient = self.u1, expected_count = 2)
+
+ question = self.post_question(user = self.u2)
+ answer = self.post_answer(user = self.u1, question = question)
+ answer.score = min_votes - 1
+ answer.save()
+ self.u2.upvote(answer)
+ #no badge when asker != answerer
+ self.assert_have_badge(
+ 'self-learner',
+ recipient = self.u1,
+ expected_count = 2
+ )
+
+ def test_civic_duty_badge(self):
+ settings.update('CIVIC_DUTY_BADGE_MIN_VOTES', 2)
+ question = self.post_question(user = self.u1)
+ answer = self.post_answer(user = self.u2, question = question)
+ answer2 = self.post_answer(user = self.u1, question = question)
+ self.u3.upvote(question)
+ self.u3.downvote(answer)
+ self.assert_have_badge('civic-duty', recipient = self.u3)
+ self.u3.upvote(answer2)
+ self.assert_have_badge('civic-duty', recipient = self.u3, expected_count = 1)
+ self.u3.downvote(answer)
+ self.assert_have_badge('civic-duty', recipient = self.u3, expected_count = 1)
+
+ def test_scholar_badge(self):
+ question = self.post_question(user = self.u1)
+ answer = self.post_answer(user = self.u2, question = question)
+ self.u1.accept_best_answer(answer)
+ self.assert_have_badge('scholar', recipient = self.u1)
+ answer2 = self.post_answer(user = self.u2, question = question)
+ self.u1.accept_best_answer(answer2)
+ self.assert_have_badge(
+ 'scholar',
+ recipient = self.u1,
+ expected_count=1
+ )
+
+ def assert_enlightened_badge_works(self, trigger):
+ self.assert_accepted_answer_badge_works(
+ 'enlightened',
+ min_score = settings.ENLIGHTENED_BADGE_MIN_UPVOTES,
+ expected_count = 1,
+ trigger = trigger
+ )
+ self.assert_accepted_answer_badge_works(
+ 'enlightened',
+ min_score = settings.ENLIGHTENED_BADGE_MIN_UPVOTES,
+ expected_count = 1,
+ previous_count = 1,
+ trigger = trigger
+ )
+
+ def assert_guru_badge_works(self, trigger):
+ self.assert_accepted_answer_badge_works(
+ 'guru',
+ min_score = settings.GURU_BADGE_MIN_UPVOTES,
+ expected_count = 1,
+ trigger = trigger
+ )
+ self.assert_accepted_answer_badge_works(
+ 'guru',
+ min_score = settings.GURU_BADGE_MIN_UPVOTES,
+ previous_count = 1,
+ expected_count = 2,
+ trigger = trigger
+ )
+
+ def test_enlightened_badge1(self):
+ self.assert_enlightened_badge_works('upvote_answer')
+
+ def test_enlightened_badge2(self):
+ self.assert_enlightened_badge_works('accept_best_answer')
+
+ def test_guru_badge1(self):
+ self.assert_guru_badge_works('upvote_answer')
+
+ def test_guru_badge1(self):
+ self.assert_guru_badge_works('accept_best_answer')